剑指offer【复杂链表的复制】

题目描述
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

具体操作看图,这个随机指针的意思是在该链表上除了一个正常的维持线性结构的指针,还有一个让链表变得更复杂的无用指针指向了该链表结构的任意一个节点。我们无需知道该指针的作用或者存在的意义。

如果直接复制普通链表,那么遍历的过程中不断new一个同样值的节点就可以构造出来,但是这个随机指针就刁钻在你不知道该指针指向链表中的哪个位置,也就是说你要以最快的方式调出该随机指针指向的节点,并给复制出来的新节点的随机指针存储。

思路是首先在原链表上全部复制出另一个自己。

然后这样就变成了新表和旧表共存的状态,遍历新表,即每个节点next的next。

于是第一遍遍历即复制表。

第二遍,将复制随机节点,因为新表和旧表的两个相同节点是相邻的,容易想到,一个新节点的随机节点即,原节点的随机节点的next的next,通过这样的方式,利用旧链表的信息快速找到新链表的需要节点的位置。指过去即可

最后一遍,新旧链表拆开,这个操作也很方便,只需将当前结点p指向新节点的next即可,而新节点的next在指针p后移后,也指向其p->next。

在这里插入图片描述
自己写的时候想将随机节点的赋值和拆表在一轮遍历中搞定,但是这样是不行的,想到如果前面的节点赋值完随机节点后拆开了,但是后面的节点的随机指针指向前面的节点,会导致新节点随机指针指到旧链表的节点上。

牛客网上的判题方法应该是将旧链表删除,然后重新遍历新链表,并对比遍历结果。如果出现了上面的错误,可能某个节点的随机节点是链表头,那么会导致整个链表被删除。

因此还是需要三次遍历来完成链表的复制

代码如下:

/*
struct RandomListNode {
    int label;
    struct RandomListNode *next, *random;
    RandomListNode(int x) :
            label(x), next(NULL), random(NULL) {
    }
};
*/
class Solution {
public:
    RandomListNode* Clone(RandomListNode* pHead)
    {
        if(pHead==NULL)return pHead;
        RandomListNode* Head=pHead;
        while(pHead!=NULL)
        {
            RandomListNode* tmp=new RandomListNode(pHead->label);
            tmp->next=pHead->next;
            pHead->next=tmp;
            pHead=tmp->next;
            //printf("%d",tmp->label);
        }
        pHead=Head;
        while(pHead!=NULL)
        {
            RandomListNode* tmp=pHead->next;
            if(pHead->random)tmp->random=pHead->random->next;
            pHead=tmp->next;
        }
        pHead=Head;
        RandomListNode* ans=Head->next;
        while(pHead!=NULL)
        {
            RandomListNode* tmp=pHead->next;
            if(tmp==NULL)break;
            pHead->next=tmp->next;
            pHead=tmp->next;
            if(pHead!=NULL)tmp->next=pHead->next;
        }
        return ans;
    }
};


原文地址:https://www.cnblogs.com/kuronekonano/p/11135670.html