剑指 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。
提示:
-10000 <= Node.val <= 10000
Node.random
为空(null
)或指向链表中的节点。- 节点数目不超过
1000
。
注意:
本题与主站 138 题相同:https://leetcode-cn.com/problems/copy-list-with-random-pointer/
解题思路
我们假设原链表如下图所示,为了图简洁清晰,我们简化了示例1
中的图,只让13
和11
的random
不为空,此外不管是next
指针还是random
指针,指向空的均未画出,红色表示random
指针。
第一步: 我们可以将每个节点都先复制一遍(此时复制时,random
指针暂时不管),并接在原节点和其下一个节点的中间,如下图:
第二步: 复制random
节点,遍历原链表的每一个节点,让p.next.random = p.random.next;
如图中蓝色的13
节点,当执行p.next.random = p.random.next
后,就是绿色的13
节点的random
指针指向了绿色的7
节点,也即完成了一个节点的random
指针的复制,后面的节点以此类推。
第三步: 分开两个链表,并将原链表还原。
Java代码
/*
// Definition for a Node.
class Node {
int val;
Node next;
Node random;
public Node(int val) {
this.val = val;
this.next = null;
this.random = null;
}
}
*/
class Solution {
public Node copyRandomList(Node head) {
if(head == null) return null;
//第一步:复制每个节点,并插在原节点和其下一个节点之间
Node cur = head;
while(cur != null){
Node copy = new Node(cur.val);
copy.next = cur.next;
cur.next = copy;
cur = cur.next.next;;
}
//第二步:复制random指针
cur = head;
while(cur != null ){
if(cur.random != null){
cur.next.random = cur.random.next;
}
cur = cur.next.next;
}
//第三步:分开两个链表,并将原链表还原。
Node dummy = new Node(-1);//复制链表的虚拟头节点
Node help = dummy;//辅助建立复制的链表
cur = head;
while(cur != null){
help.next = cur.next;
help = help.next;
cur.next = cur.next.next;
cur = cur.next;
}
return dummy.next;
}
}
解题思路二
- 先复制主链表结构,并在遍历的过程中用
map
记录下原链表节点与新链表节点的映射关系 - 这样第二次遍历原链表时,可以找到原链表节点的
Random
指向哪个节点,而这个节点同样会映射到新链表的某个节点 - 从而就可以知道新链表的
Random
节点应该指向哪里了
Go代码
/**
* Definition for a Node.
* type Node struct {
* Val int
* Next *Node
* Random *Node
* }
*/
func copyRandomList(head *Node) *Node {
// 先复制主链表结构,并在遍历的过程中用map记录下原链表节点与新链表节点的映射关系
// 这样第二次遍历原链表时,可以找到原链表节点的Random指向哪个节点,这个节点同样会映射到新链表的某个节点
// 从而就可以知道新链表的Random节点应该指向哪里了
if head == nil {
return nil
}
m := make(map[*Node]*Node)
dummy := &Node{}
cur := dummy
h := head
for h != nil {
newNode := &Node{Val:h.Val}
cur.Next = newNode
// 记录映射关系
m[h] = newNode
cur = cur.Next
h = h.Next
}
// 完成新链表各节点的random节点指向
// 原链表每个节点都可以映射到新链表某个节点,故遍历原链表,找到新链表各节点,完成Random的连接
cur = head
for cur != nil {
newNode := m[cur]
newNodeRandom := m[cur.Random]
newNode.Random = newNodeRandom
cur = cur.Next
}
return dummy.Next
}