复杂链表定义
本来想将这个问题和32“二叉排序树转链表”一起写的,但是这里要展开的部分太多,而且这个问题本身也是相对困难的,所以还是分开写吧。
首先是复杂链表的定义,我找了一段java的
public class RandomListNode {
int val;
RandomListNode next;
RandomListNode random;
public RandomListNode(int val){
this.val=val;
}
}
或者是:
class RandomListNode:
def __init__(self, x):
self.label = x
self.next = None
self.random = None
简单的说,就是除了一个向下的指针以外,还有个乱指的指针,再找一个图就能很好的说明了。
思路
首先肯定是判断空链表,如果链表为空链表,则返回本身即可。
由于random指针随便指,所以时间复杂度最麻烦的方法就是二重遍历,当然也有很多的文章给出了一个技巧,这里也沿用这个技巧,这些文章中,请参考:
复杂链表的复制 -python编写
这个我认为写得最好,总体来说就是分三步走:
1、复制原始链表的每个结点, 将复制的结点链接在其原始结点的后面
注意这里要写成这样:
pNode.next = pCloned # 插入克隆节点
pNode = pCloned.next # 下一位
2、将复制后的链表中的克隆结点的random指针链接到被克隆结点random指针的后一个结点
先pNode = pHead,然后一个while循环可以解决。
3、拆分链表:将原始链表的结点组成新的链表, 复制结点组成复制后的链表
用pClonedHead = pClonedNode = pNode.next来克隆链表,然后分奇数位和偶数位分别处理,用一个while循环写成这样:
while pNode:
pClonedNode.next = pNode.next # 偶数位的节点
pClonedNode = pClonedNode.next # 自身向前移动
pNode.next = pClonedNode.next # 奇数位的节点
pNode = pNode.next # 自身向前移动
4、代码组合,return pClonedHead
此时Head已经指向pNode.next,而pNode.next指向完整链表。
注意复制链表的最后一个结点的next指针不能跟原链表指向同一个空结点None,next指针要重新赋值None
请一定一定要自己画图!
完整代码
# offer31-solution1
class Solution:
# 返回 RandomListNode
def Clone(self, pHead):
if pHead == None:
return None
self.CloneNodes(pHead) # 复制
self.ConnectRandomNodes(pHead) # 构造random指针
return self.ReconnectNodes(pHead) # 拆分
def CloneNodes(self, pHead):
'''
复制原始链表的每个结点, 将复制的结点链接在其原始结点的后面
'''
pNode = pHead
while pNode:
pCloned = RandomListNode(0)
pCloned.label = pNode.label
pCloned.next = pNode.next # 完成cloned节点的复制
pNode.next = pCloned # 插入克隆节点
pNode = pCloned.next # 下一位
def ConnectRandomNodes(self, pHead):
'''
将复制后的链表中的克隆结点的random指针链接到被克隆结点random指针的后一个结点
'''
pNode = pHead
while pNode:
pCloned = pNode.next
if pNode.random != None:
pCloned.random = pNode.random.next
pNode = pCloned.next
def ReconnectNodes(self, pHead):
'''
拆分链表:将原始链表的结点组成新的链表, 复制结点组成复制后的链表
'''
pNode = pHead
pClonedHead = pClonedNode = pNode.next # 克隆链表
pNode.next = pClonedNode.next
pNode = pNode.next
while pNode:
pClonedNode.next = pNode.next # 偶数位的节点
pClonedNode = pClonedNode.next # 自身向前移动
pNode.next = pClonedNode.next # 奇数位的节点
pNode = pNode.next # 自身向前移动
return pClonedHead # 此时Head已经指向pNode.next,而pNode.next指向完整链表
剩下的…
仔细观察的话会发现我使用了一个# offer31-solution1
我在leetcode上看到这道题的时候,一开始绞尽脑汁写出了如上的代码(的辣鸡版,上面这版改好看了很多)。然后去看看大牛们的题解,看到了递归的思路。
大体而言,其思路是先复制当前节点,然后复制random指针,在其进行下一节点的复制的时候,调用自身,这样可以遍历到链表最深处从而完成random复制。
当然,我大体上是理解了,但是我自己写不出来……
# offer31-solution2
def Clone(self, pHead): # 递归
if pHead == None:
return None
newNode = RandomListNode(pHead.label) # 复制当前节点
newNode.random = pHead.random # 复制random指针
newNode.next = self.Clone2(pHead.next) # 其下一节点的复制,调用自身,这样可以遍历到链表最深处从而完成random复制
return newNode