首頁>技術>

劍指 Offer 06.從尾到頭列印連結串列

思考:利用棧“先進後出”的性質。遍歷連結串列,儲存結點值。遍歷輸出棧值。業務程式碼還是很容易寫的。

引申可見 劍指 Offer 24 .反轉連結串列

為了在IDEA上方便除錯,需要實現“nums2ListNode”,即陣列轉連結串列的業務程式碼。

自己寫的程式的規格結構。

 private static ListNode nums2ListNode(int[] nums) {     // 1.首先先去建立頭結點     ListNode head = new ListNode(nums[0]);     // 2.建立結點 node 去遍歷陣列元素     ListNode node = head;     // 3.從陣列的第2個元素依次遍歷     for (int i = 1; i < nums.length; i++) {         // 4.建立臨時結點         ListNode temp = new ListNode(nums[i]);         node.next = temp;         // 更新 node 節點         node = temp;     }     return head; }

為了在IDEA上方便除錯,需要實現“printListNode”,即連結串列內容列印的業務程式碼。

 private static void printListNode(ListNode head) {     StringBuffer res = new StringBuffer();     while (head.next != null) {         res.append(head.val + "->");         head = head.next;     }     res.append(head.val);     System.out.println(res); }

完整程式碼:

ArrayList<Integer>如何轉換為int[]陣列 https://blog.csdn.net/huanghanqian/article/details/73920439 這個是java8的特性 int[] res = list.stream().mapToInt(Integer::intValue).toArray();

 import java.util.ArrayList; import java.util.LinkedList; import java.util.Stack;  public class Offer06 {     public static void main(String[] args) {         int[] nums = new int[]{1, 3, 2};         ListNode head = nums2ListNode(nums);         printListNode(head);         System.out.println(reversePrint(head));     }      private static void printListNode(ListNode head) {         StringBuffer res = new StringBuffer();         while (head.next != null) {             res.append(head.val + "->");             head = head.next;         }         res.append(head.val);         System.out.println(res);     }      private static ListNode nums2ListNode(int[] nums) {         // 1.首先先去建立頭結點         ListNode head = new ListNode(nums[0]);         // 2.建立結點 node 去遍歷陣列元素         ListNode node = head;         // 3.從陣列的第2個元素依次遍歷         for (int i = 1; i < nums.length; i++) {             // 4.建立臨時結點             ListNode temp = new ListNode(nums[i]);             node.next = temp;             // 更新 node 節點             node = temp;         }         return head;     }      public static int[] reversePrint(ListNode head) {         Stack<Integer> stack = new Stack<>();         ArrayList<Integer> list = new ArrayList<>();         while (head != null) {             stack.push(head.val);             head = head.next;         }         while (!stack.isEmpty()) {             list.add(stack.pop());         }         // ArrayList<Integer>如何轉換為int[]陣列 https://blog.csdn.net/huanghanqian/article/details/73920439         // 這個是java8的特性         int[] res = list.stream().mapToInt(Integer::intValue).toArray();         return res;     } } 

ListNode的程式碼放在外邊

 public class ListNode {     int val;     ListNode next;      ListNode(int x) {         val = x;     } }
劍指 Offer 18 .刪除連結串列的節點

思考:這個考慮可能刪除頭結點,尾節點,等情況,需要採用“虛擬頭結點”+“快慢指標”的操作。

 import java.util.ArrayList; import java.util.Stack;  public class Offer18 {     public static void main(String[] args) {         int[] nums = new int[]{4, 5, 1, 9};         ListNode head = nums2ListNode(nums);         printListNode(head);         ListNode resHead = deleteNode(head, 5);         printListNode(resHead);     }      private static void printListNode(ListNode head) {         StringBuffer res = new StringBuffer();         while (head.next != null) {             res.append(head.val + "->");             head = head.next;         }         res.append(head.val);         System.out.println(res);     }      private static ListNode nums2ListNode(int[] nums) {         // 1.首先先去建立頭結點         ListNode head = new ListNode(nums[0]);         // 2.建立結點 node 去遍歷陣列元素         ListNode node = head;         // 3.從陣列的第2個元素依次遍歷         for (int i = 1; i < nums.length; i++) {             // 4.建立臨時結點             ListNode temp = new ListNode(nums[i]);             node.next = temp;             // 更新 node 節點             node = temp;         }         return head;     }      public static ListNode deleteNode(ListNode head, int val) {         // 1.建立虛擬頭結點         ListNode dummyNode = new ListNode(0);         dummyNode.next = head;         // 2.建立快慢指標         ListNode fast = dummyNode.next;         ListNode slow = dummyNode;         // 3.遍歷查詢需要刪除的節點         while (fast != null) {             // 一個要刪除的節點的值             if (fast.val == val) {                 // 該節點的前一個節點指向該節點的後一個節點                 slow.next = fast.next;                 // 將該節點的next指向null。這裡好像是加不加都可以。                 fast.next = null;                 break;             }             // 更新節點             slow = slow.next;             fast = fast.next;         }         // 這裡是考慮到頭結點就是要刪除的節點的情況         return dummyNode.next;     } }
劍指 Offer 22 .連結串列中倒數第k個節點

思考:“快慢指標”,先讓快指標走k步,然後快慢指標同時向前走,當快指標到達連結串列末尾,慢指標指向了目標節點。

 public class Offer22 {     public static void main(String[] args) {         int[] nums = new int[]{1, 2, 3, 4, 5};         ListNode head = nums2ListNode(nums);         printListNode(head);         ListNode resHead = getKthFromEnd(head, 2);         printListNode(resHead);     }      private static void printListNode(ListNode head) {         StringBuffer res = new StringBuffer();         while (head.next != null) {             res.append(head.val + "->");             head = head.next;         }         res.append(head.val);         System.out.println(res);     }      private static ListNode nums2ListNode(int[] nums) {         // 1.首先先去建立頭結點         ListNode head = new ListNode(nums[0]);         // 2.建立結點 node 去遍歷陣列元素         ListNode node = head;         // 3.從陣列的第2個元素依次遍歷         for (int i = 1; i < nums.length; i++) {             // 4.建立臨時結點             ListNode temp = new ListNode(nums[i]);             node.next = temp;             // 更新 node 節點             node = temp;         }         return head;     }      public static ListNode getKthFromEnd(ListNode head, int k) {         // 1.建立快慢指標         ListNode fast = head;         ListNode slow = head;         // 2.先讓快指標走k步         for (int i = 0; i < k; i++) {             fast = fast.next;         }         // 3.然後快慢指標同時向前走,當快指標到達連結串列末尾,慢指標指向了目標節點。         while (fast != null) {             fast = fast.next;             slow = slow.next;         }         return slow;     } }
劍指 Offer 24 .反轉連結串列

思考:“雙指標”,1.儲存cur的下一個節點。2.轉向,後節點指向前節點。3.更新節點,兩個節點都向前走一步。這個做題步驟特別統一。不能採用“虛擬頭結點”,pre應該直接設定為null。

 public class Offer24 {     public static void main(String[] args) {         int[] nums = new int[]{1, 2, 3, 4, 5};         ListNode head = nums2ListNode(nums);         printListNode(head);         ListNode resHead = reverseList(head);         printListNode(resHead);     }      private static void printListNode(ListNode head) {         StringBuffer res = new StringBuffer();         while (head.next != null) {             res.append(head.val + "->");             head = head.next;         }         res.append(head.val);         System.out.println(res);     }      private static ListNode nums2ListNode(int[] nums) {         // 1.首先先去建立頭結點         ListNode head = new ListNode(nums[0]);         // 2.建立結點 node 去遍歷陣列元素         ListNode node = head;         // 3.從陣列的第2個元素依次遍歷         for (int i = 1; i < nums.length; i++) {             // 4.建立臨時結點             ListNode temp = new ListNode(nums[i]);             node.next = temp;             // 更新 node 節點             node = temp;         }         return head;     }      public static ListNode reverseList(ListNode head) {         // 1.建立雙指標         ListNode pre = null;         ListNode cur = head;         while (cur != null) {             // 2.儲存cur的下一個節點             ListNode nextNode = cur.next;             // 3.轉向,後節點指向前節點。             cur.next = pre;             // 4.更新節點,兩個節點都向前走一步。             pre = cur;             cur = nextNode;         }         // 經過while,cur指向為null,pre指向尾節點。         return pre;     } }
劍指 Offer 25 .合併兩個排序的連結串列

思考:新建一個連結串列,比較連結串列l1和l2的值,依次將元素新增到新連結串列中。

【力扣官方將這個題目歸為了分治演算法】

先去寫 newNode = newNode.next;是不行的,newNode.next現在是null,必須先去newNode.next=l1,這樣給newNode.next賦值,才能更新newNode節點:newNode = newNode.next;

 public class Offer25 {     public static void main(String[] args) {         int[] nums1 = new int[]{1, 2, 4};         int[] nums2 = new int[]{1, 3, 4};         ListNode l1 = nums2ListNode(nums1);         ListNode l2 = nums2ListNode(nums2);         printListNode(l1);         printListNode(l2);         ListNode resHead = mergeTwoLists(l1, l2);         printListNode(resHead);     }      private static void printListNode(ListNode head) {         StringBuffer res = new StringBuffer();         while (head.next != null) {             res.append(head.val + "->");             head = head.next;         }         res.append(head.val);         System.out.println(res);     }      private static ListNode nums2ListNode(int[] nums) {         // 1.首先先去建立頭結點         ListNode head = new ListNode(nums[0]);         // 2.建立結點 node 去遍歷陣列元素         ListNode node = head;         // 3.從陣列的第2個元素依次遍歷         for (int i = 1; i < nums.length; i++) {             // 4.建立臨時結點             ListNode temp = new ListNode(nums[i]);             node.next = temp;             // 更新 node 節點             node = temp;         }         return head;     }      public static ListNode mergeTwoLists(ListNode l1, ListNode l2) {         ListNode dummyNode = new ListNode(0);         ListNode newNode = dummyNode;         // 迴圈結束條件 && 使得l1,l2其中一個為空時,則跳出while迴圈。         while (l1 != null && l2 != null) {             // 獲取l1,l2中較小的節點,新增到新連結串列 newNode 中。             if (l1.val < l2.val) {                 newNode.next = l1;                 // 更新連結串列 l1                 l1 = l1.next;             } else {                 newNode.next = l2;                 // 更新連結串列 l2                 l2 = l2.next;             }             // 更新新連結串列 newNode             newNode = newNode.next;          }         // l1,l2其中一個為空。         newNode.next = l1 != null ? l1 : l2;         return dummyNode.next;     } }
劍指 Offer 35 .複雜連結串列的複製 【需要重刷】

思考:這個題目難度明顯比前面的幾道題目要難一點。程式碼參考自1:+程式碼參考自2,java版本:

首先,先新建一個Node類

 public class Node {     int val;     Node next;     Node random;      public Node(int val) {         this.val = val;         this.next = null;         this.random = null;     } }

測試用例:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]。明顯,自己不能用IDEA去建立測試用例。

1.雜湊表方法,影片講解如下:bilibili影片講解:小美圖解:java版 小美圖解劍指offer35題 複雜連結串列的複製 演算法刷題。這個影片多看幾次,加深理解。

首先利用 HashMap 來一個不用思考的程式碼。 遍歷第一遍連結串列,我們不考慮連結串列之間的相互關係,僅僅生成所有節點,然後把它存到 HashMap 中,val 作為 key,Node 作為 value。 遍歷第二遍連結串列,將之前生成的節點取出來,更新它們的 next 和 random 指標。

 import java.util.HashMap;  public class Offer35 {     public static void main(String[] args) {         System.out.println(" 測試用例:`head = [[7,null],[13,0],[11,4],[10,2],[1,0]]`。");         System.out.println("明顯,自己不能用IDEA去建立測試用例。");     }      public Node copyRandomList(Node head) {         // 給定的連結串列為空(空指標),因此返回 null。         if (head == null) {             return null;         }         // 新建 HashMap 遍歷儲存 連結串列的節點+節點值。         // 鍵值對都是Node型別。         HashMap<Node, Node> map = new HashMap<>();         Node cur = head;         // 複製各節點,並建立 “原節點 -> 新節點” 的 Map 對映         while (cur != null) {             Node val = new Node(cur.val);             map.put(cur, val);             cur = cur.next;         }         // 遍歷完成後返回到頭結點         cur = head;         // 構建新連結串列的 next 和 random 指向         while (cur != null) {             if (cur.next != null) {                 map.get(cur).next = map.get(cur.next);             }             if (cur.random != null) {                 map.get(cur).random = map.get(cur.random);             }             cur = cur.next;         }         // 返回新連結串列的頭節點         return map.get(head);     } }

2.bilibili影片講解【採用了遞迴的方法。可以先不看這個解法。感覺看了下面的程式碼,更加困惑了。】來自 子爍愛學習

 import java.util.HashMap;  public class Offer35_bilibili {     public static void main(String[] args) {         System.out.println(" 測試用例:`head = [[7,null],[13,0],[11,4],[10,2],[1,0]]`。");         System.out.println("明顯,自己不能用IDEA去建立測試用例。");     }      HashMap<Node, Node> map = new HashMap<>();      public Node copyRandomList(Node head) {         if (head == null) {             return null;         }         if (map.containsKey(head)) {             return map.get(head);         }         Node tempNode = new Node(head.val);         map.put(head, tempNode);         // 這裡有一個遞迴呼叫哦。         tempNode.next = copyRandomList(head.next);         // 這裡有一個遞迴呼叫哦。上面的遞迴完成,這裡才會執行。         tempNode.random = copyRandomList(head.random);         return tempNode;     } } 
劍指 Offer 52 .兩個連結串列的第一個公共節點 【需要重刷】

思考:一種方法是:用A的一個節點,去比較B的所有節點。複雜度是O(n^2)。

 public class Solution {     public ListNode getIntersectionNode(ListNode headA, ListNode headB) {         ListNode copyheadB=headB;         while(headA!=null){             while(headB!=null){                 if(headA==headB){                     return headA;                 }else{                     headB=headB.next;                 }             }             headA=headA.next;             headB=copyheadB;         }         return null;     } }

思考:另一個巧妙的思路是:我們使用兩個指標 node1,node2 分別指向兩個連結串列 headA,headB 的頭結點,然後同時分別逐結點遍歷,當 node1 到達連結串列 headA 的末尾時,重新定位到連結串列 headB 的頭結點;當 node2 到達連結串列 headB 的末尾時,重新定位到連結串列 headA 的頭結點。這樣,當它們相遇時,所指向的結點就是第一個公共結點。

假如兩個沒有交點,兩個連結串列一直交換不會死迴圈嗎?

答:如果沒有重合部分,那麼 a 和 b 在某一時間點 一定會同時走到 null,從而結束迴圈。System.out.println(null==null);// 輸出為 true while (PA != PB) 在這裡會跳出while迴圈,到達下一步 return PA,也就是 return null。

如果有重合部分,分兩種情況。

長度相同的話, a 和 b 一定是同時到達相遇點,然後返回。 長度不同的話,較短的連結串列先到達結尾,然後指標轉向較長的連結串列。此刻,較長的連結串列繼續向末尾走,多走的距離剛好就是最開始介紹的解法,連結串列的長度差,走完之後指標轉向較短的連結串列。然後繼續走的話,相遇的位置就剛好是相遇點了。

影片講解:小美圖解劍指Offer 52題 兩個連結串列的第一個公共節點 java版本

headA:4->1->8->4->5 headB:5->0->1->8->4->5

headAB:4->1->8->4->5->5->0->1->8->4->5

headBA:5->0->1->8->4->5->4->1->8->4->5

測試用例如上圖,但是我在IDEA上不會設定測試用例,最終程式碼如下。程式碼參考自1:+程式碼參考自2:

 public class Offer52 {     public static void main(String[] args) {         int[] nums1 = new int[]{4, 1, 8, 4, 5};         int[] nums2 = new int[]{5, 0, 1, 8, 4, 5};         ListNode headA = nums2ListNode(nums1);         ListNode headB = nums2ListNode(nums2);         printListNode(headA);         printListNode(headB);         ListNode resHead = getIntersectionNode(headA, headB);         printListNode(resHead);     }      private static void printListNode(ListNode head) {         StringBuffer res = new StringBuffer();         while (head.next != null) {             res.append(head.val + "->");             head = head.next;         }         res.append(head.val);         System.out.println(res);     }      private static ListNode nums2ListNode(int[] nums) {         // 1.首先先去建立頭結點         ListNode head = new ListNode(nums[0]);         // 2.建立結點 node 去遍歷陣列元素         ListNode node = head;         // 3.從陣列的第2個元素依次遍歷         for (int i = 1; i < nums.length; i++) {             // 4.建立臨時結點             ListNode temp = new ListNode(nums[i]);             node.next = temp;             // 更新 node 節點             node = temp;         }         return head;     }      public static ListNode getIntersectionNode(ListNode headA, ListNode headB) {         ListNode PA = headA;         ListNode PB = headB;         while (PA != PB) {             PA = (PA == null ? headB : PA.next);             PB = (PB == null ? headA : PB.next);         }         return PA;     } }

7
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 我的世界基岩版1.16自定義材質包