1. 程式人生 > >Copy List with Random Pointer -- LeetCode

Copy List with Random Pointer -- LeetCode

                原題連結: http://oj.leetcode.com/problems/copy-list-with-random-pointer/
這是一道連結串列操作的題目,要求複製一個連結串列,不過連結串列的每個結點帶有一個隨機指標,指向某一個結點。
我們先介紹一種比較直接的演算法,思路是先按照複製一個正常連結串列的方式複製,複製的時候把複製的結點做一個HashMap,以舊結點為key,新節點為value。這麼做的目的是為了第二遍掃描的時候我們按照這個雜湊表把結點的隨機指標接上。這個演算法是比較容易想到的,總共要進行兩次掃描,所以時間複雜度是O(2*n)=O(n)。空間上需要一個雜湊表來做結點的對映,所以空間複雜度也是O(n)。程式碼如下: 
public RandomListNode copyRandomList(RandomListNode head) {    if(head == null)        return head;    HashMap<RandomListNode,RandomListNode> map = new HashMap<RandomListNode,RandomListNode>();    RandomListNode newHead = new RandomListNode(head.label);    map.put(head,newHead);    RandomListNode pre = newHead;    RandomListNode node = head.next;    while
(node!=null)    {        RandomListNode newNode = new RandomListNode(node.label);        map.put(node,newNode);        pre.next = newNode;        pre = newNode;        node = node.next;    }    node = head;    RandomListNode copyNode = newHead;    while(node!=null)    {        copyNode.random = map.get(node.random);        copyNode = copyNode.next;        node = node.next;    }    return
newHead;}
那麼有沒有辦法可以不用額外空間來完成這個任務呢?還是有的,前面我們需要一個雜湊表的原因是當我們訪問一個結點時可能它的隨機指標指向的結點還沒有訪問過,結點還沒有建立,所以我們需要線性的額外空間。想避免使用額外空間,我們只能通過利用連結串列原來的資料結構來儲存結點。基本思路是這樣的,對連結串列進行三次掃描,第一次掃描對每個結點進行復制,然後把複製出來的新節點接在原結點的next,也就是讓連結串列變成一個重複連結串列,就是新舊更替;第二次掃描中我們把舊結點的隨機指標賦給新節點的隨機指標,因為新結點都跟在舊結點的下一個,所以賦值比較簡單,就是node.next.random = node.random.next,其中node.next就是新結點,因為第一次掃描我們就是把新結點接在舊結點後面。現在我們把結點的隨機指標都接好了,最後一次掃描我們把連結串列拆成兩個,第一個還原原連結串列,而第二個就是我們要求的複製連結串列。因為現在連結串列是舊新更替,只要把每隔兩個結點分別相連,對連結串列進行分割即可。這個方法總共進行三次線性掃描,所以時間複雜度是O(n)。而這裡並不需要額外空間,所以空間複雜度是O(1)。比起上面的方法,這裡多做一次線性掃描,但是不需要額外空間,還是比較值的。實現的程式碼如下:
public RandomListNode copyRandomList(RandomListNode head) {    if(head == null)        return head;    RandomListNode node = head;    while(node!=null)    {        RandomListNode newNode = new RandomListNode(node.label);        newNode.next = node.next;        node.next = newNode;        node = newNode.next;    }    node = head;    while(node!=null)    {        if(node.random != null)            node.next.random = node.random.next;        node = node.next.next;    }    RandomListNode newHead = head.next;    node = head;    while(node != null)    {        RandomListNode newNode = node.next;        node.next = newNode.next;        if(newNode.next!=null)            newNode.next = newNode.next.next;        node = node.next;    }    return newHead;}
上面介紹了兩種方法來解決這個問題,第二種方法利用了原來的連結串列省去了額外空間,雖然多進行一次掃描,不過對時間複雜度量級沒有影響,還是對演算法有提高的。這個題目算是比較有難度的連結串列題目,既有基本操作,也需要一些演算法思想。