Leetcode 138. Copy List with Random Pointer(一个单链表包含一个指向任意结点或者null的结点指针,返回这个链表的深拷贝)

A linked list is given such that each node contains an additional random pointer which could point to any node in the list or null.

Return a deep copy of the list.

方法1:

按照原链表next的顺序依次创建节点,并处理好新链表的next指针,同时把原节点与新节点的对应关系保存到一个hash_map中,然后第二次循环将random指针处理好。这种方法的时间复杂度是O(n),空间复杂度也是O(n)。

优点:但是利用hash_map的方法具有其他的优点,如果在循环中加入一个判断,就可以检测出链表中是否有循环;而第二种方法则不行,会陷入死循环。

/**
 * Definition for singly-linked list with a random pointer.
 * struct RandomListNode {
 *     int label;
 *     RandomListNode *next, *random;
 *     RandomListNode(int x) : label(x), next(NULL), random(NULL) {}
 * };
 */
class Solution {
public:
    RandomListNode *copyRandomList(RandomListNode *head) {
        if(head==NULL) return head;
        unordered_map<RandomListNode*,RandomListNode*> map;
        RandomListNode *newhead=new RandomListNode(head->label); // 创建一个新的链表头
        RandomListNode *node1=head;
        RandomListNode *node2=newhead; // node1负责指向原链表,node2负责指向新链表 
       /**  * 按照原链表的结构不断创建新的节点,并维护好next指针,将node1与node2的对应关系保存到hash_map中,
         * 以备后面维护random指针的时候,可以通过node1找到对应的node2。
         */
        while(node1!=NULL)
        {
            map[node1]=node2;
            node1=node1->next;
            if(node1) node2->next=new RandomListNode(node1->label);
            //else node2->next=NULL;
            node2=node2->next;
        }
        node1=head;
        node2=newhead;    // 继续从头开始处理random指针
        while(node1!=NULL)
        {
            if(node1->random)
                node2->random=map[node1->random];
            node1=node1->next;
            node2=node2->next;
        }
        return newhead;
    }
};

方法2:

在原链表的每个节点之后插入一个新的节点,这样原节点与新节点的对应关系就已经明确了,因此不需要用hash_map保存,但是需要第三次循环将整个链表拆分成两个。这种方法的时间复杂度是O(n),空间复杂度是O(1)。

s思路: 
1. 如果正常的链表,要拷贝就容易:从左往右访问,遇到一个节点,就产生一个新节点放在新链表尾部。这道题的链表就不“正常”,每个节点还可能指向前面已经生成的节点或后面还没产生的节点。如果第一遍先不管random指针,先按照正常链表产生;然后再处理random指针。问题是:旧链表之间的random指针如何复制到新链表中去?这个方法,把新旧链表完全独立开来,旧链表的random指针就无法复制到新链表了。 
2. 上面的方法新建一个链表,新旧链表没有任何联系,所以导致raondom指针无法复制到新的链表。参考了答案,不得不说被shock了。如下图,新建的链表先放在旧链表的每个节点后面,而不是单独另起炉灶搞个新摊子,每个新节点都跟在原来节点屁股后面,这样新旧链表关系就非常密切,而不是割裂开来!等所有的新节点都放好后,就很容易把就链表的random指针复制到新链表,最后删除老节点即可!

这里写图片描述 
3. 如果说从头开始新建一个链表将完全和旧的链表没有关系,那么这种方式,把每个新的节点插入在旧链表中,就可以完全利用新旧之间的关系。我们还可以说,旧链表在复制的过程中,充当了scaffold的功能。新旧的交叉起来,就像修房子时,房子是被脚手架支撑起来的,修房子的过程,就是围绕脚手架修,脚手架和房子相互穿插,而不是完全独立。等房子修好后,再把脚手架拆除。回到这个问题,房子就是要的新的链表,而脚手脚就是旧的链表。 
这里写图片描述 
4. 还有一点想说的是,这种把多个过程交叉而不是完全割裂的做法才是问题的本来面貌,其他做法只是试图简化,最终看不到问题的全貌。所以做题的时候,一定要搞清楚,什么是问题的原貌,什么是人为的简化!突然想起爱因斯坦的一句名言:我们应该使问题简单到本来面貌即可,而不是试图把复杂问题简化。深有体会啊!

"Everything should be made as simple as possible, but not simpler."
  • 1
  • 2
//方法1:把旧链表当成新链表的scaffold!
class Solution {
public:
    RandomListNode *copyRandomList(RandomListNode *head) {
        //
        if(!head) return head;
        RandomListNode* p=head;
        while(p){
            RandomListNode* node=new RandomListNode(p->label);
            node->next=p->next;
            p->next=node;
            p=node->next;    
        }
        p=head;
        while(p){
            p->next->random=p->random?p->random->next:NULL;//千年老bug,不容易重视:不能保证p->random存在,就要添加保护措施,不能“裸奔”
            p=p->next->next;    
        }

        RandomListNode* newhead=head->next;

        p=head->next;
        while(p){//把新旧链表分开,旧链表重新组装成原来的样子,而不是随意扔掉!

            head->next=p->next;
            head=head->next;
            p->next=head?head->next:NULL;//千年老bug,不容易重视:不能保证head存在,就要添加保护措施,不能“裸奔”
            p=p->next;  
        }
        return newhead;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值