剑指Offer_#35_复杂链表的复制
Contents
题目
请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。
例如:
提示:
- -10000 <= Node.val <= 10000
- Node.random 为空(null)或指向链表中的节点。
- 节点数目不超过 1000 。
思路分析
方法1:哈希表
- 构建哈希表
沿着next
指针,逐个遍历旧链表节点,克隆出一个新链表节点。向哈希表添加<旧节点,新节点>
的键值对。 - 构造新链表
从头节点开始,遍历旧链表节点,然后通过哈希表获得旧节点所对应的新节点,以及旧节点的next
节点/random
节点所对应的新节点。设置每个新节点的next
和random
。
方法2:原地修改链表
- 在每个节点后克隆一个新节点
- 设置新节点的
random
指针,指向应该指向的位置 - 修改
next
指针,将拷贝的链表分离出来
图解
解答
解答1:哈希表
class Solution {
public Node copyRandomList(Node head) {
HashMap<Node,Node> map = new HashMap<>();
Node cur = head;
while(cur != null){
map.put(cur,new Node(cur.val));
cur = cur.next;
}
cur = head;
while(cur != null){
map.get(cur).next = map.get(cur.next);
map.get(cur).random = map.get(cur.random);
cur = cur.next;
}
return map.get(head);
}
}
复杂度分析
时间复杂度O(n)
空间复杂度O(n)
解答2:原地修改链表
class Solution {
public Node copyRandomList(Node head) {
CloneNodes(head);
ConnectRandomNodes(head);
return ReconnectNodes(head);
}
//在每个原节点之后增加一个克隆的节点
public void CloneNodes(Node head){
Node cur = head;
while(cur != null){
Node cloned = new Node(cur.val);
cloned.next = cur.next;
cur.next = cloned;
cur = cloned.next;
}
}
//设置每个新节点的random指针
public void ConnectRandomNodes(Node head){
Node cur = head;
while(cur != null){
//当前节点的下一个节点是克隆节点
Node cloned = cur.next;
if(cur.random != null) cloned.random = cur.random.next;
//每次迭代,指针移动两个节点
cur = cloned.next;
}
}
//将拷贝的链表拆分出来
public Node ReconnectNodes(Node head){
//cur指针指向旧链表的节点
Node cur = head;
//ERROR:必须对cloneNode和cloneHead在if之外进行初始化,否则可能不被初始化
//clonedNode是新链表的指针
Node clonedNode = null;
//clonedHead是新链表头节点
Node clonedHead = null;
if(cur != null){
clonedHead = cur.next;
clonedNode = clonedHead;
//cur指针先走一步,这样才方便连接克隆的节点
cur.next = clonedNode.next;
cur = cur.next;
}
//cur非null,那么cloneNode一定也非null
while(cur != null){
//连接到下个克隆节点
clonedNode.next = cur.next;
//更新cloneNode指针
clonedNode = clonedNode.next;
//连接到下个旧链表节点
cur.next = clonedNode.next;
//更新cur指针
cur = cur.next;
}
return clonedHead;
}
}
复杂度分析
时间复杂度O(n)
空间复杂度O(1)
TIP:避免链表迭代中的空指针异常
比较容易出现的问题是空指针异常,防止这个异常的方式就是增加一个条件判断。具体举例说明:
- 因为要访问
cur.random.next
,所以必须保证cur.random
不是null
if(cur.random != null) cloned.random = cur.random.next;
- 因为要访问cur.next,所以必须保证
cur
不是null
if(cur != null){
clonedHead = cur.next;
clonedNode = clonedHead;
//cur指针先走一步,这样才方便连接克隆的节点
cur.next = clonedNode.next;
cur = cur.next;
}
也就是说,每次些链表循环的代码,一定要注意循环过程中,*.next
或者*.val
之类的语句,一定要再其之前增加条件判断语句if(* != null)