描述
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针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;
}
};