剑指offer:复杂链表的复制

题目描述
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

# -*- coding: utf-8 -*-
# @Time         : 2019-07-05 15:52
# @Author       : Jayce Wong
# @ProjectName  : job
# @FileName     : complexListNodeClone.py
# @Blog         : https://blog.51cto.com/jayce1111
# @Github       : https://github.com/SysuJayce

class RandomListNode:
    def __init__(self, x):
        self.label = x
        self.next = None
        self.random = None

"""
    解法1:
    直接复制链表的话,由于每个节点有一个随机指针指向任意的位置(包括空指针),因此如果用最朴素的方法
    来解决,需要在将所有节点复制完之后,对每个节点的random属性遍历一次整个链表,因此假设共有n个
    节点,那么这种最朴素的解法的时间复杂度为O(n^2)

    解法2:
    解法1之所以效率低是因为每个节点的random指针的指向需要遍历整个链表才能找到,如果我们牺牲空间来
    换时间的话,那么就可以做到时间复杂度为O(n), 额外使用空间O(n)。
    具体做法可以是用一个字典来保存每个节点及其对应的克隆节点的地址,这样就可以通过查询这个哈希表在
    O(1)的时间内找到random指针所指向的节点

    解法3:
    解法2之所以能把时间复杂度降下来,是因为保存了原始节点和对应克隆节点的位置关系,因此可以很快找到
    原始节点对应的克隆节点在哪。如果我们在复制链表的时候就让克隆节点跟在原始节点后面,那么就可以在
    不额外使用哈希表的情况下做到时间复杂度为O(n)了
"""

class Solution2:
    def Clone(self, pHead):
        if not pHead:
            return None
        nodeTable = dict()  # 用于保存原始节点对应的克隆节点的地址
        pClone = RandomListNode(pHead.label)
        # 由于节点类型无法哈希,因此用地址作为key
        nodeTable[id(pHead)] = pClone

        pNode = pHead
        pNode = pNode.next
        cloneNode = pClone
        # 这个循环用于将原始链表复制出来,但是先忽略random指针,关键在于要用这个字典保存
        # 原始节点和对应克隆节点的地址
        while pNode:
            cloneNode.next = RandomListNode(pNode.label)
            nodeTable[id(pNode)] = cloneNode.next
            cloneNode = cloneNode.next
            pNode = pNode.next

        # 根据字典保存的信息设置克隆链表的random指针
        cloneNode = pClone
        while pHead:
            # 需要注意的是random指针可能是指向None,而我们在字典中并没有保存None的key
            if pHead.random:
                cloneNode.random = nodeTable[id(pHead.random)]
            pHead = pHead.next
            cloneNode = cloneNode.next

        return pClone

class Solution3:
    def Clone(self, pHead):
        # 解法3的思路可以分为三步:
        # 1. 复制整个链表,这里先忽略random指针的指向,得到形如A->A‘->B->B‘->C->C‘的复制结果
        # 2. 设置克隆节点的random指针
        # 3. 将链表拆分成原始链表和克隆链表
        self.cloneNode(pHead)
        self.connectSiblingNode(pHead)
        return self.reconnectNode(pHead)

    def cloneNode(self, pHead):
        pNode = pHead
        while pNode:
            pClone = RandomListNode(pNode.label)
            pClone.next = pNode.next
            pNode.next = pClone
            pNode = pClone.next

    def connectSiblingNode(self, pHead):
        pNode = pHead
        while pNode:
            clone_head = pNode.next
            if pNode.random:
                clone_head.random = pNode.random.next
            pNode = clone_head.next

    def reconnectNode(self, pHead):
        if not pHead:
            return None
        new_head = pHead.next
        pNode = new_head
        """
        这里不知为什么,如果把pHead指向new_head的左边(即pHead和new_head分别指向A和A‘)
        然后进入循环就不能通过牛客的OJ,

        但是将pHead指向new_head的右边(即pHead和new_head分别指向B和A‘)
        然后进入循环就可以通过。

        这两种方法在本地调试的时候都是没问题的。
        """
        pHead.next = pNode.next
        pHead = pHead.next
        while pHead:
            pNode.next = pHead.next
            pNode = pNode.next
            pHead.next = pNode.next
            pHead = pHead.next

        return new_head

def main():
    # 1->3
    # 2->1
    # 3->2
    # node = RandomListNode(1)
    # node.next = RandomListNode(2)
    # node.next.next = RandomListNode(3)
    # node.random = node.next.next
    # node.next.random = node
    # node.next.next.random = node.next

    node1 = RandomListNode(1)
    node2 = RandomListNode(2)
    node3 = RandomListNode(3)
    node4 = RandomListNode(4)
    node5 = RandomListNode(5)
    node1.next = node2
    node2.next = node3
    node3.next = node4
    node4.next = node5
    node1.random = node3
    node2.random = node5
    node4.random = node2

    solution = Solution2()
    head = solution.Clone(node1)
    while head:
        if head.random:
            print(head.label, head.random.label)
        else:
            print(head.label, ‘None‘)
        head = head.next

if __name__ == ‘__main__‘:
    main()

原文地址:https://blog.51cto.com/jayce1111/2417753

时间: 2024-08-01 12:15:21

剑指offer:复杂链表的复制的相关文章

剑指offer 复杂链表的复制

题目描述: 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head.(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空) 思路:要进行深拷贝,需要每次都new一个节点出来,利用unordered_map, 2.3 138. Copy List with Random Pointer https://leetcode.com/problems/copy-list-with-random-poi

剑指offer之链表

//剑指offer 之 链表 //面试题6 从尾到头打印链表 /***************************************************************************************** 问题描述: 输入一个链表的头节点,从尾到头反过来打印出每个节点的值 链表节点定义如下: struct ListNode{ int m_nValue; ListNode* m_pNext; }; *******************************

【剑指offer】链表倒数第k个节点

转载请注明出处:http://blog.csdn.net/ns_code/article/details/25662121 在Cracking the Code Interview上做过了一次,这次在九度OJ上测试,AC. 题目描述: 输入一个链表,输出该链表中倒数第k个结点.(hint: 请务必使用链表.) 输入: 输入可能包含多个测试样例,输入以EOF结束.对于每个测试案例,输入的第一行为两个整数n和k(0<=n<=1000, 0<=k<=1000):n代表将要输入的链表元素的

剑指offer (5) 链表插入删除

我们在操作链表的时候,必须注意以下事项: 1. 链表指针为NULL的情况 2. 插入删除涉及到 链表第一个节点时,需要修改 链表的第一个节点: a. 因为 c语言都是传值的,如果需要修改一个变量,就必须通过 指向该变量的指针(即该变量的地址) 例如:例如 修改 int a,则输入参数必须是 int* a, 修改a 则是: *a = b; (b为int) 我们需要修改一个指针时,就必须通过 该指针的地址,也就是 指向该指针的指针,即二级指针 例如  修改 int* a, 则输入参数必须是 int*

剑指Offer--026-复杂链表的复制

链接 牛客OJ:复杂链表的复制 九度OJ:http://ac.jobdu.com/problem.php?pid=1524 GitHub代码: 026-复杂链表的复制 CSDN题解:剑指Offer–026-复杂链表的复制 牛客OJ 九度OJ CSDN题解 GitHub代码 复杂链表的复制 1524-复杂链表的复制 剑指Offer–026-复杂链表的复制 026-复杂链表的复制 题意 题目描述 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点)

【Java】 剑指offer(22) 链表中倒数第k个结点

正文 本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集   题目 输入一个链表,输出该链表中倒数第k个结点.为了符合大多数人的习惯,本题从1开始计数,即链表的尾结点是倒数第1个结点.例如一个链表有6个结点,从头结点开始它们的值依次是1.2.3.4.5.6.这个链表的倒数第3个结点是值为4的结点. 思路 第一直觉是先从头开始遍历,计算链表个数n,然后重新遍历,第n-k+1个结点即为所需要的结点.但是需要遍历2次.后面采用了栈进行实现该

两个链表的第一个公共结点(剑指offer)+链表

两个链表的第一个公共结点 参与人数:1171时间限制:1秒空间限制:32768K 通过比例:31.25% 最佳记录:0 ms|0K(来自  running) 题目描述 输入两个链表,找出它们的第一个公共结点. 链接:http://www.nowcoder.com/practice/6ab1d9a29e88450685099d45c9e31e46?rp=2&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking

剑指Offer24 复杂链表的复制

1 /************************************************************************* 2 > File Name: 24_ComplexListClone.cpp 3 > Author: Juntaran 4 > Mail: [email protected] 5 > Created Time: 2016年08月31日 星期三 14时24分35秒 6 ********************************

25、剑指offer--复杂链表的复制

题目描述 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head.(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空) 思路:直接把复制的node放在原node的后面,这样结构变为: 上面为第一次遍历,第二次遍历时把红色的新node的random域赋值,规则是: newNode->ranodm = oldNode->random->next; 然后第三次遍历把上面的链表拆分为两个即可

剑指Offer 14. 链表中倒数第k个结点 (链表)

题目描述 输入一个链表,输出该链表中倒数第k个结点. 题目地址 https://www.nowcoder.com/practice/529d3ae5a407492994ad2a246518148a?tpId=13&tqId=11167&rp=3&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking 思路 三个特例:如果输入的链表为空:k大于链表的长度:k为0的情况.对于正常情况,设置两个指针分