有两个clone图和链表的题。基本的思路是,用一个hashmap来维护新旧对应的节点的映射关系。138那个题除了这种基本方法,还可以不用hashmap直接修改指针。
133. Clone Graph
给一个联通无向图的reference。node的定义:
class Node {
public int val;
public List neighbors;
} 求一个这个图的deep copy。
思路是:由旧图的reference point开始遍历旧图(数据结构给的是neighbor list,就用recursive DFS即可,代码结构简单),每到一个节点,先检查hashmap里是否已经有这个节点的cloned copy了(之前制造过了),有就直接用,没有就制造一个和当前值相同的cloned copy,并把<旧节点,新节点>的映射关系存入hashmap。
凡是记录,都是为了防止重复计算。这个hashmap,也是为了防止某个节点是好多个人的Neighbor,然后制造了很多个cloned copies,就很浪费了。
class Solution {
Map<Node, Node> map = new HashMap<>();//<old,new>
public Node cloneGraph(Node node) {
return clone(node);
}
private Node clone(Node node) {//输入旧节点,返回对应的新节点
if (node == null) {return node;}
if (map.containsKey(node)) {
return map.get(node);
}
Node newNode = new Node(node.val);
map.put(node, newNode);
for (Node neighbor : node.neighbors) {
newNode.neighbors.add(clone(neighbor));
}
return newNode;
}
}
138. Copy List with Random Pointer
这个题138和上面133的区别在于,133是graph,访问是乱七八糟的,有可能重复访问,而138主体是一个list,访问顺序很清楚,依次访问就可以依次制造出cloned copy,不需要hashmap。
需要hashmap的是random pointer。用hashmap记录<旧节点,新节点>映射关系,新节点在找random pointer的指向的时候,因为其对应的旧节点的random pointer的指向是已知的,于是,
- 通过hashmap先找到其对应的旧节点(这一步没法实现,因为是reverse查找hashmap,不支持)
- 找旧节点random pointer的指向
- 指向的那个节点的对应的新节点,就是要求的“新节点random pointer的指向”。
第一步那个实现不了的怎么解决呢?把“制造新节点”和“连接random pointer”两个工作一起做。 这样,在制造好新节点的那个时刻,其对应旧节点的reference还在(因为新节点就是在遍历到旧节点的那个时刻制造出来的),就可以解决第一步“找旧节点”的问题了。
于是,list一共遍历两遍:
- 开始的撸一遍旧list来先制造copy“主体”(即节点仅由next pointer连接)的过程,实际上并不连接next pointer,只是new出来一个新节点,放进hashmap里
- 而“连接next pointer”的工作,被留着和“连接random pointer”一起当成后面第二轮撸。
class Solution {
public Node copyRandomList(Node head) {
Map<Node, Node> map = new HashMap<>(); //<oldnode, newnode>
//create new nodes, put to map
Node cur = head;
while (cur != null) {
map.put(cur, new Node(cur.val));
cur = cur.next;
}
//connect next and random
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);
}
}
还可以不用hashmap,直接修改指针。
7->7->13->13->…->1->1->null
- 在原list的每个节点后面clone出一个新节点
- 依次给新节点的random pointer赋值
- 断开新旧混合的list,提取出deep copy的新链表(记得添加dummy)
class Solution {
public Node copyRandomList(Node head) {
Node iter = head, tempNext;
//step 1: 在原list的每个节点后面clone出一个新节点
while (iter != null) { //7->7->13->13->...->1->1->null
tempNext = iter.next; //tempNext:13(1st)
Node copy = new Node(iter.val);//copy:7(2nd)
iter.next = copy;//iter:7(1st)
copy.next = tempNext;
iter = tempNext;
}
//step 2: 依次给新的节点的random pointer赋值
iter = head;
while (iter != null) {
if (iter.random != null) {
iter.next.random = iter.random.next;
}
//move pointer
iter = iter.next.next;
}
//step 3: 断开新旧混合的list,提取出deep copy的新链表
iter = head;
Node dummy = new Node(-1);
Node cur, pre = dummy;
while (iter != null) {//7->7->13->13->...->1->1->null
tempNext = iter.next.next;//tempNext:13
//extract the copy
cur = iter.next;//iter:7(1st),cur:7(2nd)
pre.next = cur;
pre = cur;
//restore original list
iter.next = tempNext;//7(1st)->13(1st)
iter = tempNext;
}
return dummy.next;
}
}