LeetCode 138:複製帶隨機指標的連結串列 Copy List with Random Pointer

愛寫Bug發表於2019-07-29

給定一個連結串列,每個節點包含一個額外增加的隨機指標,該指標可以指向連結串列中的任何節點或空節點。

要求返回這個連結串列的深拷貝

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.

示例:

img

輸入:
{"$id":"1","next":{"$id":"2","next":null,"random":{"$ref":"2"},"val":2},"random":{"$ref":"2"},"val":1}

解釋:
節點 1 的值是 1,它的下一個指標和隨機指標都指向節點 2 。
節點 2 的值是 2,它的下一個指標指向 null,隨機指標指向它自己。

提示:

  1. 你必須返回給定頭的拷貝作為對克隆列表的引用。

Note:

  1. You must return the copy of the given head as a reference to the cloned list.

解題思路:

由於需要考慮隨機指標,隨機指標指向的節點可能是null,也可能是連結串列的最後一個節點,深拷貝下,你不能將新連結串列的隨機指標指向原連結串列的節點,所以無法一遍得到新連結串列。

兩種解題方法,一種是拷貝所有節點,暫存在一種資料結構內,然後再遍歷一遍該資料結構,找到拷貝的節點,確定隨機指標的指向。因為每個節點都要找到隨機指標指向的節點,如果藉助 雜湊表(字典) 其查詢複雜度為 O(1) ,這樣可以把總時間複雜度降到 O(n) ,由於要暫存所有節點,其空間複雜度為 O(n)。

另一種解題方法是需要三次遍歷得到結果,簡單來說就是先把每個節點拷貝,並把拷貝節點連線到原節點後面,依次類推。確定隨機節點的關係之後再拆分連結串列。是一種很巧妙的思路:

原連結串列:1->2->3
複製每個節點到原節點後面:1->1->2->2->3->3
複製隨機指標的指向關係
拆分連結串列:1->2->3, 1->2->3

複製隨機指標指向時,原節點的隨機節點下一個節點即為新節點的隨機節點。假如原連結串列:1->2->3,節點1的隨機節點為3,複製節點之後:1->1->2->2->3->3

我們只需將新節點(原節點1的後一個節點1)指向原節點的隨機節點的後一個節點(原節點的隨機節點為3,而複製之後隨機節點3的後一個節點依然為3,但這個節點為新節點),最後拆分連結串列(連結串列拆分不影響節點指向關係)。其時間複雜度為 O(n),空間複雜度為 O(1)。

第一種方法:

Java:

class Solution {
    public Node copyRandomList(Node head) {
        if (head == null) return null;
        HashMap<Node, Node> map = new HashMap<>();//藉助hashMap
        Node newHead = new Node(0);//虛擬頭節點
        Node cur = newHead;//指標指向當前節點
        Node tmp;
        while (head != null) {
            if (map.containsKey(head)) {//查詢原節點是否已存在於map
                tmp = map.get(head);//如果存在直接取value值
            } else {
                tmp = new Node(head.val);//不存在則新建一個值相同的節點
                map.put(head, tmp);//存入map,key為原節點,value為新節點
            }
            cur.next = tmp;
            if (head.random != null) {//原節點的隨機節點不為空的情況下
                if (map.containsKey(head.random)) {//查詢該隨即節點是否已存在於map
                    tmp.random = map.get(head.random);//存在則直接將新節點的隨機指標指向其value值
                } else {
                    tmp.random = new Node(head.random.val);//不存在則新建一個值相同的隨機節點
                    map.put(head.random, tmp.random);//存入map,key為原節點的隨機節點,value為新節點的隨機節點
                }
            }
            head = head.next;//重新整理原連結串列當前頭節點
            cur = tmp;//即cur=cur.next,重新整理新連結串列當前節點
        }
        return newHead.next;
    }
}

Python3:

class Solution:
    def copyRandomList(self, head: 'Node') -> 'Node':
        if not head: return None
        map = {}
        newHead = Node(val=0, next=None, random=None)
        cur = newHead
        while head:
            if head in map.keys():
                tmp = map.get(head)
            else:
                tmp = Node(val=head.val, next=None, random=None)
                map.setdefault(head, tmp)
            cur.next = tmp
            if head.random:
                if head.random in map.keys():
                    tmp.random = map.get(head.random)
                else:
                    tmp.random = Node(val=head.random.val, next=None, random=None)
                    map.setdefault(head.random, tmp.random)
            head = head.next
            cur = tmp
        return newHead.next

第二種方法:

Java:

class Solution {
    public Node copyRandomList(Node head) {
        if (head == null) return null;
        //複製節點 1->2->3 ==> 1->1->1->2->2->3->3
        Node cur = head;
        while (cur != null) {
            Node next = cur.next;
            Node tmp = new Node(cur.val);
            cur.next = tmp;
            tmp.next = next;
            cur = next;
        }
        //複製隨機指標
        cur = head;
        while (cur != null) {
            //cur.next.random=>新節點的隨機節點  cur.random.next=>原節點的隨機節點的下一個節點
            cur.next.random = cur.random == null ? null : cur.random.next;
            cur = cur.next.next;//連結串列擴充套件一倍後肯定是偶數位連結串列,所以可以直接用cur.next.next
        }
        //分離節點
        cur = head;
        Node newHead = head.next;//新連結串列頭節點
        while (cur != null) {
            Node tmp = cur.next;
            cur.next = tmp.next;
            cur = cur.next;
            tmp.next = cur == null ? null : cur.next;
        }
        return newHead;
    }
}

Python3:

class Solution:
    def copyRandomList(self, head: 'Node') -> 'Node':
        if not head: return None
        cur = head
        while cur:
            next = cur.next
            tmp = Node(val=cur.val, next=next, random=None)
            cur.next = tmp
            cur = next
        cur = head
        while cur:
            cur.next.random = cur.random.next if cur.random else None
            cur = cur.next.next
        cur = head
        newHead = head.next
        while cur:
            tmp = cur.next
            cur.next = tmp.next
            cur = cur.next
            tmp.next = cur.next if cur else None
        return newHead

相關文章