剑指 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 。
方法一:HashMap
一个简单的想法就是分为两步走, 先把next指针全部指向相应的克隆的节点, 然后再处理random指针
遍历原链表节点并赋值节点, 将前驱节点的指针修改为克隆的节点。这样一趟遍历完成后, 所有next指针全部指向正确的位置了。那么random指针的指向问题如何处理呢?
-
简单来想, 我可以设置一个计数器只是某个链表的节点是第几个节点。这样我依据可以通过遍历链表的方式找到原链表节点random指针指向链表的第一个元素。然后依据此值,遍历克隆链表, 找到这个节点, 并相应的给克隆节点的random指针赋值即可。
-
上述的思路其实非常繁琐, 每找一个random都要遍历一遍原链表和克隆链表, N个节点要遍历N次。就是为了找节点之间的对应关系, 面对这样的问题, 我们只需要在第一次克隆链表, 赋值Next指针的过程中, 把原节点和克隆节点的对应关系保存到HaspMap里, 就能通过原节点的Random指针, 迅速找到可能节点的Random指向元素的指针了。比如对应关系分别是(A1,A2), (B1,B2), (C1,C2)... (Z1,Z2)。如果A1的random指针指向Z1, 那么我立马就可以根据节点的对应关系, 让克隆节点A2的random指针指向Z2, 这样random指针的复制也只需要遍历一遍数据就可以全部完成了。
方法二
其实方法一的时间复杂度已经优化不错了, 但是空间复杂度还有可优化的空间。
这种方法把步骤分为3步:
-
复制节点, 并且将复制的节点放在原节点的后面, 先用next指针串起来
-
遍历链表, 赋值random指针。因为克隆节点的random指向的节点可以通过原节点的random指针的next指针直接得到。所以这一步的可以直接赋值。
-
然后将链表的Next指针还原, 并拆分成两个链表
其实这种方法的核心思想是和第一种HashMap的思想是一致的, 那就是保存原节点和克隆节点之间的对应关系, 方法一使用额外的HashMap来保存对应关系, 而这种方法,使用next指针保存对应关系。
public Node copyRandomList(Node head) {
if (head == null) {
return null;
}
cloneNode(head);
connectSibling(head);
return splitLinkedList(head);
}
// 克隆链表, 并将克隆的节点, 修改原节点的next指向克隆节点
public void cloneNode(Node head) {
Node p = head;
while(p != null) {
Node pClone = new Node(p.val);
Node nextNode = p.next;
p.next = pClone;
pClone.next = nextNode;
p = nextNode;
}
}
// 连接random指针
public void connectSibling(Node head) {
Node p = head;
while(p != null) {
// 这里要注意random指针可能为空, 要额外判断
p.next.random = ((p.random != null) ? p.random.next : null);
p = p.next.next;
}
}
// 将链表还原回来 next指针重新指向下一个节点
public Node splitLinkedList(Node head) {
Node headClone = head.next;
Node p = head;
while(p != null) {
Node nextNode = p.next.next;
Node pClone = p.next;
p.next = nextNode;
pClone.next = nextNode != null ? nextNode.next : null;
p = nextNode;
}
return headClone;
}