请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。
解题思路
题目分析:这道题的关键在于每个节点都包含一个random指针,指向随机的节点,而需要给random复制的时候,random所指向的复制节点很有可能还没有创建,如何处理random指针问题是这道题的关键所在。
解法一:暴力解法
思路
这里我第一个想到的就是先复制链表,然后再遍历链表对节点中的random指针赋值。这里需要注意的是怎么确定random指针要指向的节点是哪一个
- 用地址肯定不行,因为复制过来的节点地址不一样
- 用val值也不行,如果有多个相同的val值就会导致指向的节点和原链表不一致
- 用索引是可以的,链表是有序的,所以索引可以用来确定复制链表中的节点对应原链表中的哪个节点
所以,整个函数设计如下:
- 先复制链表,不处理random指针
- 同时遍历两个两个链表
- 确定原链表中节点random指向节点的索引值
- 让复制链表中的节点的random指向相同索引位置的节点
- 复制完成
代码
class Solution {
public Node copyRandomList(Node head) {
//1.先复制链表,不处理random指针
Node chead = new Node(0);
Node ccur = chead;
Node cpre = null;
Node cur = head;
while (cur != null) {
cpre = ccur;
ccur = new Node(cur.val);
cpre.next = ccur;
cur = cur.next;
}
chead = chead.next;
//2.同时遍历两个两个链表
ccur = chead;
cur = head;
while (cur != null) {
Node ran = cur.random;
Node c1 = head;
//3.确定原链表中节点random指向节点的索引值
int i = 0;
while (c1 != ran) {
c1 = c1.next;
i++;
}
//4.让复制链表中的节点的random指向相同索引位置的节点
Node c2 = chead;
for (int j = 0; j < i; j++) {
c2 = c2.next;
}
ccur.random = c2;
cur = cur.next;
ccur = ccur.next;
}
//5.复制完成
return chead;
}
}
解法二:回溯+哈希表
思路
我们从头节点开始复制,val值直接复制即可,而next和random节点还没有创建,所以我们需要创建next和random节点,创建next和random节点继续重复这个过程即可。
但是仍然有一个问题,就是random指针的指向是随机的,多个random指针有可能指向同一个节点,所以我们需要用一个哈希表来存储已经创建了的节点,并在每次给next和random指针赋值前检查所指向的节点是否已经创建了,如果没有,我们就递归执行直到节点已经创建或者为null,然后回溯,直到所有节点都创建完成。至此,复杂链表的复制就完成了。
函数设计:
- 基线条件:节点为null或者已经创建了
- 递归条件:节点还没有创建
代码
class Solution {
HashMap<Node,Node> nodeCache = new HashMap<>();
public Node copyRandomList(Node head) {
if (head==null) return null;
while(!nodeCache.containsKey(head)){
Node newHead = new Node(head.val);
nodeCache.put(head,newHead);
newHead.next = copyRandomList(head.next);
newHead.random = copyRandomList(head.random);
}
return nodeCache.get(head);
}
}