Copy List with Random Pointer

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

你可能感兴趣的:(Random)