Idiot-maker

  :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

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节点,方法依然是要依赖于原链表。但是思路非常巧妙,值得体会。

posted on 2015-03-31 21:10  NickyYe  阅读(201)  评论(0编辑  收藏  举报