列表

详情


剑指 Offer 35. 复杂链表的复制

请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null

 

示例 1:

输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]

示例 2:

输入:head = [[1,1],[2,1]]
输出:[[1,1],[2,1]]

示例 3:

输入:head = [[3,null],[3,0],[3,null]]
输出:[[3,null],[3,0],[3,null]]

示例 4:

输入:head = []
输出:[]
解释:给定的链表为空(空指针),因此返回 null。

 

提示:

 

注意:本题与主站 138 题相同:https://leetcode.cn/problems/copy-list-with-random-pointer/

 

原站题解

去查看

上次编辑到这里,代码来自缓存 点击恢复默认模板
/* // Definition for a Node. class Node { public: int val; Node* next; Node* random; Node(int _val) { val = _val; next = NULL; random = NULL; } }; */ class Solution { public: Node* copyRandomList(Node* head) { } };

golang 解法, 执行用时: 0 ms, 内存消耗: 3.5 MB, 提交时间: 2022-11-13 11:30:24

/**
 * Definition for a Node.
 * type Node struct {
 *     Val int
 *     Next *Node
 *     Random *Node
 * }
 */
 
/*
本题要求我们对一个特殊的链表进行深拷贝。
如果是普通链表,我们可以直接按照遍历的顺序创建链表节点。
而本题中因为随机指针的存在,当我们拷贝节点时,
「当前节点的随机指针指向的节点」可能还没创建,因此我们需要变换思路。
一个可行方案是,我们利用回溯的方式,让每个节点的拷贝操作相互独立。
对于当前节点,我们首先要进行拷贝,然后我们进行「当前节点的后继节点」和「当前节点的随机指针指向的节点」拷贝,
拷贝完成后将创建的新节点的指针返回,即可完成当前节点的两指针的赋值。

具体地,我们用哈希表记录每一个节点对应新节点的创建情况。
遍历该链表的过程中,我们检查「当前节点的后继节点」和「当前节点的随机指针指向的节点」的创建情况。
如果这两个节点中的任何一个节点的新节点没有被创建,我们都立刻递归地进行创建。
当我们拷贝完成,回溯到当前层时,我们即可完成当前节点的指针赋值。
注意一个节点可能被多个其他节点指向,因此我们可能递归地多次尝试拷贝某个节点,
为了防止重复拷贝,我们需要首先检查当前节点是否被拷贝过,
如果已经拷贝过,我们可以直接从哈希表中取出拷贝后的节点的指针并返回即可。

在实际代码中,我们需要特别判断给定节点为空节点的情况。

*/

var cachedNode map[*Node]*Node

func deepCopy(node *Node) *Node {
    if node == nil {
        return nil
    }
    if n, has := cachedNode[node]; has {
        return n
    }
    newNode := &Node{Val: node.Val}
    cachedNode[node] = newNode
    newNode.Next = deepCopy(node.Next)
    newNode.Random = deepCopy(node.Random)
    return newNode
}

func copyRandomList(head *Node) *Node {
    cachedNode = map[*Node]*Node{}
    return deepCopy(head)
}

golang 解法, 执行用时: 0 ms, 内存消耗: 3.3 MB, 提交时间: 2022-11-13 11:29:56

/**
 * Definition for a Node.
 * type Node struct {
 *     Val int
 *     Next *Node
 *     Random *Node
 * }
 */
 
/*
我们首先将该链表中每一个节点拆分为两个相连的节点,
例如对于链表 A→B→C,我们可以将其拆分 A→A′→B→B′→C→C′。 
对于任意一个原节点 S,其拷贝节点 S' 即为其后继节点。
这样,我们可以直接找到每一个拷贝节点 S' 的随机指针应当指向的节点,
即为其原节点 S 的随机指针指向的节点 T 的后继节点 T' 。
需要注意原节点的随机指针可能为空,我们需要特别判断这种情况。

当我们完成了拷贝节点的随机指针的赋值,我们只需要将这个链表按照原节点与拷贝节点的种类进行拆分即可,只需要遍历一次。
同样需要注意最后一个拷贝节点的后继节点为空,我们需要特别判断这种情况。

*/

func copyRandomList(head *Node) *Node {
    if head == nil {
        return nil
    }
    for node := head; node != nil; node = node.Next.Next {
        node.Next = &Node{Val: node.Val, Next: node.Next}
    }
    for node := head; node != nil; node = node.Next.Next {
        if node.Random != nil {
            node.Next.Random = node.Random.Next
        }
    }
    headNew := head.Next
    for node := head; node != nil; node = node.Next {
        nodeNew := node.Next
        node.Next = node.Next.Next
        if nodeNew.Next != nil {
            nodeNew.Next = nodeNew.Next.Next
        }
    }
    return headNew
}

上一题