JZ25 复杂链表的复制

该博客探讨了如何使用Python实现深拷贝一个包含随机指针的复杂链表。介绍了深拷贝和浅拷贝的区别,并提供了两种解决方案:一是利用Python的`copy`模块,二是使用哈希表法。哈希表法通过遍历原链表,创建新链表并维护映射关系,然后设置新链表的random指针。代码中还展示了C++的实现方式。

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

描述

输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针random指向一个随机节点),请对此链表进行深拷贝,并返回拷贝后的头结点。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)。 下图是一个含有5个结点的复杂链表。图中实线箭头表示next指针,虚线箭头表示random指针。为简单起见,指向null的指针没有画出。

首先对深拷贝进行了解,

浅拷贝(copy):拷贝父对象,不会拷贝对象的内部的子对象。

深拷贝(deepcopy): copy 模块的 deepcopy 方法,完全拷贝了父对象及其子对象。 

来源于《Python 直接赋值、浅拷贝和深度拷贝解析:https://www.runoob.com/w3cnote/python-understanding-dict-copy-shallow-or-deep.html》,作者有更详细的解析图。

字典浅拷贝:

实例

>>> a = {1: [1,2,3]}

>>> b = a.copy()

>>> a, b ({1: [1, 2, 3]}, {1: [1, 2, 3]})

>>> a[1].append(4)

>>> a, b ({1: [1, 2, 3, 4]}, {1: [1, 2, 3, 4]})

深度拷贝需要引入 copy 模块:

实例

>>>import copy

>>> c = copy.deepcopy(a)

>>> a, c ({1: [1, 2, 3, 4]}, {1: [1, 2, 3, 4]})

>>> a[1].append(5)

>>> a, c ({1: [1, 2, 3, 4, 5]}, {1: [1, 2, 3, 4]})

所以这道题如果直接使用Python的库,自测也能运行通过。

import copy
class Solution:
    def Clone(self, pHead):
        chead=copy.deepcopy(pHead)
        return chead

但还是要学一下另外一种思想:哈希表法

①首先遍历一遍原链表,将原链表的各个节点、各个节点的值、以及各个节点指向的random节点各自存放到三个新的链表中。

 class RandomListNode:
     def __init__(self, x):
         self.label = x
         self.next = None
         self.random = None
class Solution:
    def Clone(self, head):
        nodeList = []     #存放各个节点
        randomList = []   #存放各个节点指向的random节点。没有则为None
        labelList = []    #存放各个节点的值

        while head:
            randomList.append(head.random)
            nodeList.append(head)
            labelList.append(head.label)
            head = head.next
        #random节点的索引,如果没有则为1    
        labelIndexList = map(lambda c: nodeList.index(c) if c else -1, randomList)

        dummy = RandomListNode(0)
        pre = dummy
        #节点列表,只要把这些节点的random设置好,顺序串起来就ok了。
        nodeList=map(lambda c:RandomListNode(c),labelList)
        #把每个节点的random绑定好,根据对应的index来绑定
        for i in range(len(nodeList)):
            if labelIndexList[i]!=-1:
                nodeList[i].random=nodeList[labelIndexList[i]]
        for i in nodeList:
            pre.next=i
            pre=pre.next
        return dummy.next

②代码中有一句:

labelIndexList = map(lambda c: nodeList.index(c) if c else -1, randomList)

关于lambda...if...else(if...else...)的解析在https://blog.youkuaiyun.com/weixin_40683253/article/details/87278483博客中有实例解析。

意思就是:

map(lambda x, y: x + y, [1,2,3,4,5], [6,7,8,9,10])

输出为:[7,9,11,13,15]

所以这里的代码是nodeList.index(randomList),为了找到random节点的索引

③ 创建LabelList的random,因为每个值都有一个random指针。

接着对应的索引来绑定好random。

讨论组另有C/C++:

/*
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 NULL;
        
        //定义一个哈希表
        unordered_multimap<RandomListNode*,RandomListNode*> table;
        
        // 开辟一个头结点
        RandomListNode* pClonedHead=new RandomListNode(pHead->label);
        pClonedHead->next=NULL;
        pClonedHead->random=NULL;
        
        // 将头结点放入map中
        table.insert(make_pair(pHead,pClonedHead));
        
        //设置操作指针
 		RandomListNode* pNode=pHead->next;
        RandomListNode* pClonedNode=pClonedHead;
        
        // 第一遍先将简单链表复制一下
        while(pNode!=NULL)
        {
            // 不断开辟pNode的拷贝结点
            RandomListNode* pClonedTail=new RandomListNode(pNode->label);
            pClonedTail->next=NULL;
        	pClonedTail->random=NULL;
            
            //连接新节点,更新当前节点
            pClonedNode->next=pClonedTail;
            pClonedNode=pClonedTail;
            
            //将对应关系  插入到哈希表中
            table.insert(make_pair(pNode,pClonedTail));
            
            //向后移动操作节点
            pNode=pNode->next;
        }
        
        //需从头开始设置random节点,设置操作指针
        pNode=pHead;
        pClonedNode=pClonedHead;
		
        // 根据map中保存的数据,找到对应的节点
        while(pNode!=NULL)
        {
            
            if(pNode->random!=NULL)
            {
                //找到对应节点,更新复制链表
                pClonedNode->random=table.find(pNode->random)->second;
            }
            
            //向后移动操作节点
            pNode=pNode->next;
            pClonedNode=pClonedNode->next;
        }
        
        return pClonedHead;
        
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值