题目
请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。
思路
方法一:哈希表
利用哈希表的查询特点,构建 原链表节点 和 新链表对应节点 的键值对映射关系: <原 cur 节点, 新 cur 节点>
,再遍历构建新链表各节点的 next
和 random
引用指向即可。
用哈希表dic
记录原、新节点的对应关系是为了后续新链表节点的引用指向
流程如下:
java代码如下:
class Solution {
public Node copyRandomList(Node head){
if(head == null) return null;
Node cur = head;
Map<Node,Node> map = new HashMap<>();//哈希表用来做原链表节点和新链表节点的映射关系
// 复制各节点,并建立 “原节点 -> 新节点” 的 Map 映射
while(cur != null){
map.put(cur, new Node(cur.val));
cur = cur.next;
}
//新链表复制完成后,从头开始构造新链表节点引用指向
cur = head;//从头结点开始
//构建新链表的next和random指向
while(cur != null){
map.get(cur).next = map.get(cur.next);//map.get(cur)相当于是从哈希表中获取新链表节点cur',cur为key,获得的是value即新链表节点
map.get(cur).random = map.get(cur.random);
cur = cur.next;
}
//返回新链表的头结点
return map.get(head);
}
}
方法二:拼接 + 拆分
考虑构建 原节点 1 -> 新节点 1 -> 原节点 2 -> 新节点 2 -> …… 的拼接链表,如此便可在访问原节点的 random
指向节点的同时找到新对应新节点的 random
指向节点。
- 复制各节点,构建拼接链表:
原节点 1 -> 新节点 1 -> 原节点 2 -> 新节点 2 -> ……
- 构建新链表各节点的
random
指向:当访问原节点cur
的随机指向节点cur.random
时,对应新节点cur.next
的随机指向节点为cur.random.next
- 拆分原 / 新链表:设置
pre / cur
分别指向原 / 新链表头节点,遍历执行pre.next = pre.next.next
和cur.next = cur.next.next
将两链表拆分开 - 返回新链表的头节点
res
即可
流程图如下:
java代码如下:
class Solution {
public Node copyRandomList(Node head){
if(head == null) return null;
Node cur = head;
//1.复制各节点,并构建拼接链表(相当于创建一个值等于原节点的新节点并插入进去)
while(cur != null){
Node tmp = new Node(cur.val);//创建相同值的新节点
//在原节点后插入新节点
tmp.next = cur.next;
cur.next = tmp;
//继续插入下一个新节点
cur = tmp.next;
}
// 2. 构建各新节点的 random 指向
cur = head;
while(cur != null){
if(cur.random != null){
cur.next.random = cur.random.next;//cur.next表示cur对应的新节点,cur.random.next表示cur.random对应的新节点
}
cur = cur.next.next;
}
// 3. 拆分两链表
cur = head.next;
Node pre = head;
Node res = head.next;//保存新链表头节点,用于最后返回
while(cur.next != null){
pre.next = pre.next.next;
cur.next = cur.next.next;
//继续往后判断
pre = pre.next;
cur = cur.next;
}
pre.next = null;//单独处理原链表尾节点
return res;//返回新链表头节点
}
}