剑指 Offer 35. 复杂链表的复制
题目描述:
请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个结点除了有一个 next 指针指向下一个结点,还有一个 random 指针指向链表中的任意结点或者 null。
示例:
Input: head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
Output: [[7,null],[13,0],[11,4],[10,2],[1,0]]
解题思路:
思路一: 使用hash Map 存储链表的结点,其中,原链表结点为key,value为该结点的复制结点。首先遍历一遍链表,将每个结点都进行复制。其次,将原结点的next结点通过get将其复制结点取出,并且让复制结点的next指向该结点。同理,random结点也是这样的做法。可能文字有点表达不清,通过代码注释
class Solution {
public Node copyRandomList(Node head) {
if(head == null) return null;
HashMap<Node,Node> temp = new HashMap<>(); // 创建一个哈希表来存储结点映射关系
Node cur = head;
while(cur!=null){
temp.put(cur,new Node(cur.val)); // 将原结点遍历一遍,原结点作为key,复制结点作为value。
cur = cur.next;
}
cur = head;
while(cur!=null){
temp.get(cur).next = temp.get(cur.next); // temp.get(cur) 取出的是cur对应的复制结点。 temp.get(cur.next) 取出的是cur.next对应的复制结点,通过这样形成连接关系。
temp.get(cur).random = temp.get(cur.random); // 同理。
cur = cur.next;
}
return temp.get(head);
}
}
思路二: 原地操作,不使用额外的空间存储链表。首先原地复制链表,即原结点指向该原结点的复制结点。这样的好处是,每个原结点的下一个为它的复制结点,原结点的下下个结点为其原链表的对应下一个结点。
因此在完成结点的复制后,复制后的结点的next,则可以通过原结点的next结点的next找到。比如7的next为3,那么赋值后的7’则是通过7.next=3, 3.next = 3’ 找到对应关系。同理random关系也是这么找。
class Solution {
public Node copyRandomList(Node head) {
if(head == null) return null;
Node cur = head;
Node next = null;
while(cur != null){ // 首先进行结点的复制与拼接
next = cur.next; // 先将当前结点的下一个结点保存
cur.next = new Node(cur.val); // 让当前结点的下一个结点指向它的复制结点。
cur.next.next = next; // 让复制结点的下一个结点指向原结点的下一个结点。
cur = next; // 将当前结点指针移到之前保存的位置
}
cur = head;
next = head.next;
while(cur != null){
next = cur.next; // 指向复制结点
next.random = cur.random != null ? cur.random.next : null; // 如果cur.random 不为空,说明复制结点的random也不为空,且为cur.random.next
cur = cur.next.next; // cur指针移向下一个原链表的结点。
}
Node res = head.next; // 保存结果
cur = head;
Node temp1 = null;
while(cur!=null){ // 将原链表和复制链表拆开
next = cur.next.next; // 先保存原链表的下一个结点
temp1 = cur.next; // 保存复制结点
cur.next = next; // 将原结点指向下下个结点,也就是原链表的对应关系
temp1.next = next != null ? next.next : null; // 复制链表的下一个结点为next的下一个结点,但是此时要保证next不为空,否则无法指向。
cur = next;
}
return res;
}
}
该方法需要注意的是,在循环内,当指针向下下个结点移动时,不能复制后的结点的指针一次跳两个,而应该选择原结点的下一个结点这样来作为复制结点,而不是复制结点直接跳到它的下下个结点,这样可能会产生空指针异常。
复杂链表的复制就是这样啦,可能文字方面讲得不够好,所以我在代码里加了注释,希望对你能有所帮助!
如有错误,欢迎指出,大家一起进步。