offer 35.复杂链表的复制
请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。
来源:力扣(LeetCode)
输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
输入:head = [[1,1],[2,1]]
输出:[[1,1],[2,1]]
输入:head = [[3,null],[3,0],[3,null]]
输出:[[3,null],[3,0],[3,null]]
提示:
-10000 <= Node.val <= 10000
Node.random
为空(null)或指向链表中的节点。- 节点数目不超过 1000 。
此题是一个中等题,如何按照咱们之前的方法简单链表的复制,没有random指针,直接前插法或后插法复制新链表,而此题不同有一个random指针问题就在于我们如何处理这个指针,我们先看一下难点在哪
class Solution {
public Node copyRandomList(Node head) {
Node cur = head;
Node dum = new Node(0), pre = dum;
while(cur != null) {
Node node = new Node(cur.val); // 复制节点 cur
pre.next = node; // 新链表的 前驱节点 -> 当前节点
// pre.random = "???"; // 新链表的 「 前驱节点 -> 当前节点 」 无法确定
cur = cur.next; // 遍历下一节点
pre = node; // 保存当前新节点
}
return dum.next;
}
}
绿色的地方就是我们的难点 如何处理呢?
我们第一种方法就是HashMap
利用哈希表的查询特点,考虑构建 原链表节点 和 新链表对应节点 的键值对映射关系,再遍历构建新链表各节点的 next
和 random
引用指向即可。
算法流程:
1.若头节点 head 为空节点,直接返回 nullnull ;
2.初始化: 哈希表 dic , 节点 cur 指向头节点;
3.复制链表:
4.建立新节点,并向 dic 添加键值对 (原 cur 节点, 新 cur 节点) ;
5.cur 遍历至原链表下一节点;
6.构建新链表的引用指向:
7.构建新节点的 next 和 random 引用指向;
8.cur 遍历至原链表下一节点;
9.返回值: 新链表的头节点 dic[cur] ;
class Solution { public Node copyRandomList(Node head) { if(head == null) return null; Node cur = head; Map<Node, Node> map = new HashMap<>(); // 3. 复制各节点,并建立 “原节点 -> 新节点” 的 Map 映射 while(cur != null) { map.put(cur, new Node(cur.val)); cur = cur.next; } cur = head; // 4. 构建新链表的 next 和 random 指向 while(cur != null) { map.get(cur).next = map.get(cur.next); map.get(cur).random = map.get(cur.random); cur = cur.next; } // 5. 返回新链表的头节点 return map.get(head); } }
方法二:拼接+拆分
考虑构建 原节点 1 -> 新节点 1 -> 原节点 2 -> 新节点 2 -> …… 的拼接链表,如此便可在访问原节点的 random 指向节点的同时找到新对应新节点的 random 指向节点。
1.复制各节点,构建拼接链表:
2.构建新链表各节点的 random
指向
3.拆分原 / 新链表
4.返回新链表的头节点 res
即可
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 = cur.next.next; } // 3. 拆分两链表 cur = head.next; Node pre = head, 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; // 返回新链表头节点 } }