前言
剑指offer打卡,35题,复杂链表的复制,同样可以利用链表插入和摘节点的快速操作来完成。
一、HashMap一一对应
1、思想
1)针对next指针
遍历链表,生成Node节点,并把原始节点和生成的节点做一个hashMap的<key,value>映射,也就是原Node作为Key,新对应的Node作为Value。
2)针对random指针
再次遍历链表,通过原链表的Node的random指针所指的节点作为Key,通过map查出已有的新节点Value。然后给新节点的random指针赋值。
2、源码
public class CopyRandomList35 {
//记录旧节点和新节点的一一对应
Map<Node, Node> existN = new HashMap<>();
public Node copyRandomList(Node head) {
Node point = head;
if (head == null)
return null;
Node cnode = new Node(head.val);
Node result = cnode;
existN.put(head, cnode);
Node node;
//第一遍遍历为了Next指针和用Map记录顺序
while (point.next != null) {
node = new Node(point.next.val);
existN.put(point.next, node);
cnode.next = node;
cnode = node;
if (existN.get(point.next.random) != null) {
node.random = existN.get(point.next.random);
}
point = point.next;
}
point = head;
cnode = result;
//第二遍循环通过Map的记录来给新节点的Random指针赋值
while (point != null) {
if(point.random != null && cnode.random == null) {
cnode.random = existN.get(point.random);
}
point = point.next;
cnode = cnode.next;
}
return result;
}
}
// Definition for a Node.
class Node {
int val;
Node next;
Node random;
public Node(int val) {
this.val = val;
this.next = null;
this.random = null;
}
}
二、无Map用链表自身特性
1、思想
第一种算法要用map记录新旧对应,需要额外的空间。利用链表自身插入和删除快速的特点,采用第一次遍历先复制旧节点到其后面,然后再次遍历将random指针设好,最后一次遍历来一分为二。
2、源码
// Definition for a Node.
class Node {
int val;
Node next;
Node random;
public Node(int val) {
this.val = val;
this.next = null;
this.random = null;
}
}
class CopyRandomList {
public Node copyRandomList(Node head) {
Node point = head;
if (head == null)
return null;
Node node;
//复制相同的节点在原节点后面。
while (point != null) {
node = new Node(point.val);
node.next = point.next;
point.next = node;
point = point.next.next;
}
point = head;
Node result = point.next;
//设置random指针
while (point != null) {
if(point.random!=null){
point.next.random = point.random.next;
}
point = point.next.next;
}
point = head;
//一分为二
while(point.next.next != null){
Node node1 = point.next;
point.next = point.next.next;
node1.next = point.next.next;
point = point.next;
}
point.next = null;
return result;
}
}
三、总结
Map有快速查找的功能,链表有记录顺序且删除插入都非常方便的特点。利用好各种数据结构的特性,联系到题目,便能快速解题。
补充
看了官方的解答,第一种叫回溯+hash表,这是我方法一的一种简化,第一次看到回溯的使用方法,原来是通过递归来回溯达到快速且简洁。
1、源码
class Solution {
Map<Node, Node> cachedNode = new HashMap<Node, Node>();
public Node copyRandomList(Node head) {
if (head == null) {
return null;
}
if (!cachedNode.containsKey(head)) {
Node headNew = new Node(head.val);
cachedNode.put(head, headNew);
headNew.next = copyRandomList(head.next);
headNew.random = copyRandomList(head.random);
}
return cachedNode.get(head);
}
}
这里通过递归先完成next节点的创建,然后递归回来的时候,map已经记录好了,这个时候就直接使用map的value来设置random指针。这样一来一回就完成了我三面的三步。
所以headNew.random = copyRandomList(head.random);
可以改成headNew.random = cachedNode.get(head.random);
。这样容易理解。