《剑指offer》31、带random指针的复杂链表复制

复杂链表定义

本来想将这个问题和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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值