https://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.
解题思路:
啥叫deep copy,啥叫shallow copy?Java里shallow copy的意思是,B拷贝于A,但B和A指向同一个对象。deep copy的意思是,B和A指向不同的对象,但这两个对象完全一样。
具体可以参考 http://stackoverflow.com/questions/869033/how-do-i-copy-an-object-in-java
那么这道题目的难度就在于多了一个random指针。当前节点和next很容易复制,遍历一遍,不断根据原来链表的值去new新节点,并将next指向下一节点即可。问题是,第二遍的时候,如何找到当前节点的random节点?
这里借用的是HashMap这个数据结构,第一遍遍历的时候,建立一个map,key是原链表的节点,value是当前链表的节点。我们知道,实际上map里存的是相应的地址。这样,第二遍寻找新链表当前节点node的random时,只要在map里找原链表对应节点的value就可以了。
代码如下:
/** * Definition for singly-linked list with a random pointer. * class RandomListNode { * int label; * RandomListNode next, random; * RandomListNode(int x) { this.label = x; } * }; */ public class Solution { public RandomListNode copyRandomList(RandomListNode head) { if (head == null) { return head; } Map<RandomListNode, RandomListNode> map = new HashMap<RandomListNode, RandomListNode>(); RandomListNode copyHead = new RandomListNode(head.label); RandomListNode head1 = head; RandomListNode returnHead = copyHead; map.put(head1, copyHead); while(head1.next != null) { copyHead.next = new RandomListNode(head1.next.label); map.put(head1.next, copyHead.next); copyHead = copyHead.next; head1 = head1.next; } copyHead = returnHead; head1 = head; while(copyHead != null) { copyHead.random = map.get(head1.random); copyHead = copyHead.next; head1 = head1.next; } return returnHead; } }
上面的解法应该是比较容易理解的。Google到网友还有另一种比较好的解法,可以只花额外的O(1)空间,使用同样O(n)的时间。
这么做:
1. 在原来链表的每个节点后,插入一个一模一样的复制节点。这样copy的next顺序就自然出来了。比如1-1'-2-2'-3-3'
2. 将每个复制的节点的random指向它前一个节点的random的下一个节点
3. 按next的顺序抽出所有复制的节点,形成新的链表并返回。
代码如下
/** * Definition for singly-linked list with a random pointer. * class RandomListNode { * int label; * RandomListNode next, random; * RandomListNode(int x) { this.label = x; } * }; */ public class Solution { public RandomListNode copyRandomList(RandomListNode head) { if (head == null) { return head; } RandomListNode returnHead = head; //在原链表的每个节点后都插入一个新节点 while(head != null) { RandomListNode newNode = new RandomListNode(head.label); newNode.next = head.next; head.next = newNode; head = head.next.next; } //将每个复制的节点的random指向它前一个节点的random的next head = returnHead; while(head != null) { if(head.random != null) { head.next.random = head.random.next; } head = head.next.next; } //抽出每个复制的节点,返回 head = returnHead; returnHead = returnHead.next; while(head != null) { RandomListNode newNode = head.next; head.next = head.next.next; //这个边界条件一定要注意,否则原链表只有一个节点的时候会越界 if(newNode.next != null) { newNode.next = newNode.next.next; } head = head.next; } return returnHead; } }
可以看出,上面的思想,同样是要用某种方法在新复制的链表里找到每个节点的random节点,方法依然是要依赖于原链表。但是思路非常巧妙,值得体会。