https://oj.leetcode.com/problems/copy-list-with-random-pointer/
A linked list is given such that each node contains an additional random pointer which could point to any node in the list or null.
Return a deep copy of the list.
public RandomListNode copyRandomList(RandomListNode head)
这一题,最直观的做法就是HashMap,键放的是原list的元素,值放的是克隆出来的元素。不管是遇到random pointer还是next pointer的时候先看看指向的对象是否存在于HashMap中,如果存在,就直接返回值域的内容。否则就克隆内容并将键值对放进HashMap中。下面给出对应的代码:
public RandomListNode copyRandomList(RandomListNode head) {
HashMap<RandomListNode, RandomListNode> orig_to_copy = new HashMap<RandomListNode, RandomListNode>();
RandomListNode tmp = head, res = null, copy_tmp = null;
while(tmp != null){
RandomListNode copy = new RandomListNode(tmp.label);
orig_to_copy.put(tmp, copy);
res = res == null ? copy : res;
if(tmp == head){
copy_tmp = copy;
}else{
copy_tmp.next = copy;
copy_tmp = copy_tmp.next;
}
tmp = tmp.next;
}
tmp = head;
while(tmp != null){
if(tmp.random != null){
orig_to_copy.get(tmp).random = orig_to_copy.get(tmp.random);
}
tmp = tmp.next;
}
return res;
}
这段代码的逻辑和上述算法有一点点不同,这段代码是将原链表跑两遍,第一遍是纯复制普通链表,第二遍是连接random pointer,本质和我刚才描述的算法没有不同,只是这样处理在第一个while循环里,逻辑会比较简单一些。
但这并不是最优的算法,最优的算法是这样的。
第一步,以正常链表的方式克隆并且以一种合并的状态合成一个链表。合并的方式是这样的,原链表当前节点的下一个插入自己的克隆节点。这样就会构成下面这样的链表
A->A`->B->B`->C->C`....其中X`是X的克隆。
第二步,再以这样的链表复制random pointer,在这种形态里,当前原链表的节点的random pointer指向的对象的下一个对象就是该节点克隆该指向的对象。
也就是假如A指向C,那么A`一定指向C`,也就是C的下一个节点。所以A`的random Pointer也就很好分配了。
第三步,链接完所有克隆的random node之后,进行分拆。将奇数位的node作为原链表,偶数位的node作为克隆链表分拆成两个链表,返回第二个即可。
public RandomListNode copyRandomList(RandomListNode head) {
// Good solution
RandomListNode res_head = null, res_tmp = null, tmp = head;
while(tmp != null){//第一步
RandomListNode copy = new RandomListNode(tmp.label);
copy.next = tmp.next;
tmp.next = copy;
res_head = res_head == null ? copy : res_head;
tmp = tmp.next.next;
}
tmp = head;
while(tmp != null){//第二步
if(tmp.random != null){
tmp.next.random = tmp.random.next;
}
tmp = tmp.next.next;
}
tmp = head;
res_tmp = res_head;
while(tmp != null){//第三步
tmp.next = tmp.next.next;
if(res_tmp.next != null)
res_tmp.next = res_tmp.next.next;
tmp = tmp.next;
res_tmp = res_tmp.next;
}
return res_head;
}
2018-01-20 Updated
给出一个新写的HashMap的答案,感觉更符合上面描写的算法,一个循环即可。而且感觉这两年半算法退步了不少,但是写代码的style是有进步的。
public RandomListNode copyRandomList(RandomListNode head) {
HashMap<RandomListNode, RandomListNode> nodeMap = new HashMap<RandomListNode, RandomListNode>();
RandomListNode newHead = null, travelNode = head, copyTravelNode = null;
while (travelNode != null) {
RandomListNode copiedNode = this._getCopiedNode(nodeMap, travelNode);
if (travelNode.random != null) {
RandomListNode copiedRandom = this._getCopiedNode(nodeMap, travelNode.random);
copiedNode.random = copiedRandom;
}
if (newHead == null) {
newHead = copiedNode;
copyTravelNode = newHead;
} else {
copyTravelNode.next = copiedNode;
copyTravelNode = copyTravelNode.next;
}
travelNode = travelNode.next;
}
return newHead;
}
private RandomListNode _getCopiedNode(Map<RandomListNode, RandomListNode> nodeMap, RandomListNode origin) {
if (nodeMap.containsKey(origin)) {
return nodeMap.get(origin);
} else {
RandomListNode copied = new RandomListNode(origin.label);
nodeMap.put(origin, copied);
return copied;
}
}