左神算法:复制含有随机指针节点的链表 / 复杂链表的复制(Java版本)
本題來自左神《程序員代碼面試指南》“復制含有隨機指針節點的鏈表”題目。
題目
一種特殊的鏈表節點類描述如下:
public static class Node {public int value;public Node next;public Node rand;public Node(int data) {this.value = data;} }Node 類中的value 是節點值,next 指針和正常單鏈表中next 指針的意義一樣,都指向下一個節點,rand 指針是Node 類中新增的指針,這個指針可能指向鏈表中的任意一個節點,也可能指向null。
給定一個由Node 節點類型組成的無環單鏈表的頭節點head,請實現一個函數完成這個鏈表中所有結構的復制,并返回復制的新鏈表的頭節點。例如:鏈表1->2->3->null,假設1 的rand指針指向3,2 的rand 指針指向null,3 的rand 指針指向1。復制后的鏈表應該也是這種結構,比如,1′->2′->3′->null,1′的rand 指針指向3′,2′的rand 指針指向null,3′的rand 指針指向1′,最后返回1′。
進階:不使用額外的數據結構,只用有限幾個變量,且在時間復雜度為O(N)內完成原問題要實現的函數。
題解
普通解法
首先介紹普通解法,普通解法可以做到時間復雜度為O(N),額外空間復雜度為O(N),需要使用到哈希表(HashMap)結構。具體過程如下:
1.從左到右遍歷鏈表,對每個節點都復制生成相應的副本節點,然后將對應關系放入哈希表map 中。例如,鏈表1->2->3->null,遍歷1、2、3 時依次生成1′、2′、3′,最后將對應關系放入map 中。
步驟1 完成后,原鏈表沒有任何變化,每一個副本節點的next 和rand 指針都指向null。
2.再從左到右遍歷鏈表,此時就可以設置每一個副本節點的next 和rand 指針。
例如:原鏈表1->2->3->null,假設1 的rand 指針指向3,2 的rand 指針指向null,3 的rand指針指向1。遍歷到節點1 時,可以從map 中得到節點1 的副本節點1′,節點1 的next 指向節點2,所以從map 中得到節點2 的副本節點2′,然后令1′.next=2′,副本節點1′的next 指針就設置好了。同時節點1 的rand 指向節點3,所以從map 中得到節點3 的副本節點3′,然后令1′.rand=3′,副本節點1′的rand 指針也設置好了。以這種方式可以設置每一個副本節點的next與rand 指針。
3.將1′節點作為結果返回即可。
哈希表增刪改查的操作時間復雜度都是O(1),普通方法一共只遍歷鏈表兩遍,所以普通解法的時間復雜度為O(N),因為使用了哈希表來保存原節點與副本節點的對應關系,所以額外空間復雜度為O(N)。
具體過程請參看如下代碼中的copyListWithRand1 方法。
進階解法
接下來介紹進階解法,進階解法不使用哈希表來保存對應關系,而只用有限的幾個變量完成所有的功能。具體過程如下:
1.從左到右遍歷鏈表,對每個節點cur 都復制生成相應的副本節點copy,然后把copy 放在cur 和下一個要遍歷節點的中間。
例如:原鏈表1->2->3->null,在步驟1 中完成后,原鏈表變成1->1′->2->2′->3->3′->null。
2.再從左到右遍歷鏈表,在遍歷時設置每一個副本節點的rand 指針。還是舉例來說明調整過程。
例如:此時鏈表為1->1′->2->2′->3->3′->null,假設1 的rand 指針指向3,2 的rand 指針指向null,3 的rand 指針指向1。遍歷到節點1 時,節點1 的下一個節點1.next 就是其副本節點1′。1 的rand 指針指向3,所以1′的rand 指針應該指向3′。如何找到3′呢?因為每個節點的副本節點都在自己的后一個,所以此時通過3.next 就可以找到3′,令1′.next=3′即可。以這種方式可以設置每一個副本節點的rand 指針。
3.步驟2 完成后,節點1,2,3,……之間的rand 關系沒有任何變化,節點1′,2′,3′……之間的rand 關系也被正確設置了,此時所有的節點與副本節點串在一起,將其分離出來即可。例如:此時鏈表為1->1′->2->2′->3->3′->null,分離成1->2->3->null 和1′->2′->3′->null 即可,并且在這一步中,每個節點的rand 指針不用做任何調整,在步驟2 中都已經設置好。
4.將1′節點作為結果返回即可。
進階解法考查的依然是利用有限幾個變量完成鏈表調整的代碼實現能力。具體過程請參看如下代碼中的copyListWithRand2 方法。
package chapter_2_listproblem;import java.util.HashMap;public class Problem_09_CopyListWithRandom {public static class Node {public int value;public Node next;public Node rand;public Node(int data) {this.value = data;}}public static Node copyListWithRand1(Node head) {HashMap<Node, Node> map = new HashMap<Node, Node>();Node cur = head;while (cur != null) {map.put(cur, new Node(cur.value));cur = cur.next;}cur = head;while (cur != null) {map.get(cur).next = map.get(cur.next);map.get(cur).rand = map.get(cur.rand);cur = cur.next;}return map.get(head);}public static Node copyListWithRand2(Node head) {if (head == null) {return null;}Node cur = head;Node next = null;// copy node and link to every nodewhile (cur != null) {next = cur.next;cur.next = new Node(cur.value);cur.next.next = next;cur = next;}cur = head;Node curCopy = null;// set copy node randwhile (cur != null) {next = cur.next.next;curCopy = cur.next;curCopy.rand = cur.rand != null ? cur.rand.next : null;cur = next;}Node res = head.next;cur = head;// splitwhile (cur != null) {next = cur.next.next;curCopy = cur.next;cur.next = next;curCopy.next = next != null ? next.next : null;cur = next;}return res;}public static void printRandLinkedList(Node head) {Node cur = head;System.out.print("order: ");while (cur != null) {System.out.print(cur.value + " ");cur = cur.next;}System.out.println();cur = head;System.out.print("rand: ");while (cur != null) {System.out.print(cur.rand == null ? "- " : cur.rand.value + " ");cur = cur.next;}System.out.println();}// for testpublic static void main(String[] args) {Node head = null;Node res1 = null;Node res2 = null;printRandLinkedList(head);res1 = copyListWithRand1(head);printRandLinkedList(res1);res2 = copyListWithRand2(head);printRandLinkedList(res2);printRandLinkedList(head);System.out.println("=========================");head = new Node(1);head.next = new Node(2);head.next.next = new Node(3);head.next.next.next = new Node(4);head.next.next.next.next = new Node(5);head.next.next.next.next.next = new Node(6);head.rand = head.next.next.next.next.next; // 1 -> 6head.next.rand = head.next.next.next.next.next; // 2 -> 6head.next.next.rand = head.next.next.next.next; // 3 -> 5head.next.next.next.rand = head.next.next; // 4 -> 3head.next.next.next.next.rand = null; // 5 -> nullhead.next.next.next.next.next.rand = head.next.next.next; // 6 -> 4printRandLinkedList(head);res1 = copyListWithRand1(head);printRandLinkedList(res1);res2 = copyListWithRand2(head);printRandLinkedList(res2);printRandLinkedList(head);System.out.println("=========================");} }輸出結果
order: rand: order: rand: order: rand: order: rand: ========================= order: 1 2 3 4 5 6 rand: 6 6 5 3 - 4 order: 1 2 3 4 5 6 rand: 6 6 5 3 - 4 order: 1 2 3 4 5 6 rand: 6 6 5 3 - 4 order: 1 2 3 4 5 6 rand: 6 6 5 3 - 4 =========================總結
以上是生活随笔為你收集整理的左神算法:复制含有随机指针节点的链表 / 复杂链表的复制(Java版本)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 左神算法:反转单向和双向链表(Java版
- 下一篇: 左神算法:两个单链表相交的一系列问题(链