138. Copy List with Random Pointer

本文介绍两种深拷贝含随机指针链表的方法。第一种方法利用哈希表保存原始链表的映射关系,实现新链表的构建及随机指针设置。第二种方法则更巧妙,通过在原始链表中穿插新节点,简化映射关系的维护。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:深拷贝一个链表,链表除了含有next指针外,还包含一个random指针,该指针指向字符串中的某个节点或者为空。

节点定义为:

struct RandomListNode {
int label;
RandomListNode *next, *random;
RandomListNode(int x) : label(x), next(NULL), random(NULL) {}
};

假设原始链表如下,细线表示next指针,粗线表示random指针,没有画出的指针均指向NULL:

算法1:我们在构建新链表的节点时,保存原始链表的next指针映射关系,并把指针做如下变化(蓝色为原始链表节点,紫红色为新链表节点):

然后在上图的基础上进行如下两步

1、构建新链表的random指针:比如new1->random = new1->random->random->next, new2->random = NULL, new3-random = NULL, new4->random = new4->random->random->next

2、恢复原始链表:根据最开始保存的原始链表next指针映射关系恢复原始链表

该算法时间空间复杂度均为O(N)


算法2:该算法更为巧妙,不用保存原始链表的映射关系,构建新节点时,指针做如下变化,即把新节点插入到相应的旧节点后面:

同理分两步

1、构建新节点random指针:new1->random = old1->random->next, new2-random = NULL, new3-random = NULL, new4->random = old4->random->next

2、恢复原始链表以及构建新链表:例如old1->next = old1->next->next,  new1->next = new1->next->next

该算法时间复杂度O(N),空间复杂度O(1)


下面分别为算法1和算法2代码:

class Solution {
public:
    RandomListNode *copyRandomList(RandomListNode *head)
     {
        // Note: The Solution object is instantiated only once and is reused by each test case.
        if(head == NULL)return NULL;
        std::map<RandomListNode *,RandomListNode *> oldlistMap;
        RandomListNode *result = new RandomListNode(head->label) ;
        RandomListNode *pold = head, *pnew = result;
        pnew->random = pold;
        //pold->next = pnew;
        RandomListNode* poldPre = pold, *pnewPre = pnew;
        while(pold->next != NULL)
        {
            //保存old list的next指针
            oldlistMap.insert(std::map<RandomListNode*,
                              RandomListNode*>::value_type(pold, pold->next));
            pold = pold->next;
            poldPre->next = pnew;
            pnew = new RandomListNode(pold->label);
            pnewPre->next = pnew;
            pnew->random = pold;

            poldPre = pold;
            pnewPre = pnew;
        }
        pold->next = pnew;//设置old list最后一个节点

        //设置new list的random指针
        pnew = result;
        while(pnew != NULL)
        {
            if(pnew->random->random)
                pnew->random = pnew->random->random->next;
            else pnew->random = NULL;
            pnew = pnew->next;
        }
        //恢复old list的next指针
        pold = head;
        for(int i = 1; i <= oldlistMap.size(); i++)
        {
            pold->next = oldlistMap[pold];
            pold = pold->next;
        }
        pold->next = NULL;//old list 最后一个节点

        return result;
    }
};
class Solution {
public:
    RandomListNode *copyRandomList(RandomListNode *head)
     {
        // Note: The Solution object is instantiated only once and is reused by each test case.
        if(head == NULL)return NULL;
        RandomListNode *result = NULL;
        RandomListNode *pold = head, *pnew = result, *poldNext = NULL;
        do
        {
            poldNext = pold->next;
            pnew = new RandomListNode(pold->label);
            pold->next = pnew;
            pnew->next = poldNext;

            if(result == NULL)
                result = pnew;
            pold = poldNext;
        }while(pold);
        //设置new list的random指针
        pold = head;
        while(pold)
        {
            if(pold->random)
                pold->next->random = pold->random->next;
            pold = pold->next->next;
        }
        //恢复old list 和new list
        pold = head;
        pnew = result;
        while(pnew->next)
        {
            pold->next = pnew->next;
            pold = pold->next;
            pnew->next = pold->next;
            pnew = pnew->next;
        }
        pold->next = NULL;
        pnew->next = NULL;
        return result;
    }
};


9.18更新

------------------

思路:


这题的关键是如何track一个节点是否已经被copy了。假如我们要copy如下list,用指针p1来扫描每个节点,另一个指针p2建立copy。
______
|           |
|          V
1->2->3

p1扫描1时,p2复制1,以及1->next (2), 1->random (3)。之后p1, p2分别移到各自的2节点。此时我们必须得知道节点3在之前已经被复制了,并且得知道复制节点的地址。
______
|           |
|          V
1->2    3 

所以这里可以使用一个hash table来记录原节点和复制节点的地址对应关系。这样每次要建立当前节点p的next和random前,先在hash table中查找。如果找到,则直接连接;否则建立新节点连上,并把和原节点的对应关系存入hash table中。
class Solution {
public:
    RandomListNode *copyRandomList(RandomListNode *head) {
        if(!head) return NULL;
        unordered_map<RandomListNode*, RandomListNode*> ht;
        RandomListNode *p1 = head;
        RandomListNode *p2 = new RandomListNode(head->label);
        ht[head] = p2;
        while(p1) {
            if(p1->next) {
                if(ht.count(p1->next))
                    p2->next = ht[p1->next];
                else {
                    p2->next = new RandomListNode(p1->next->label);
                    ht[p1->next] = p2->next;
                }
            }
            if(p1->random) {
                if(ht.count(p1->random))
                    p2->random = ht[p1->random];
                else {
                    p2->random = new RandomListNode(p1->random->label);
                    ht[p1->random] = p2->random;
                }
            }
            p1 = p1->next;
            p2 = p2->next;
        }
        return ht[head];
    }
};


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值