1. 程式人生 > 其它 >LeetCode.148 排序連結串列

LeetCode.148 排序連結串列

方法一:自頂向下的歸併排序(遞迴法)

1. 分割:找到當前連結串列中點,並從中點將連結串列斷開(以便下次遞迴時,連結串列片段擁有正確邊界);

  • 首先對連結串列進行分割,使用fast,slow快慢雙指標法,奇數個節點找到中點,偶數個節點找到中心左邊的節點,對連結串列進行斷開。
  • 找到重點slow後,執行slow.next = null 將連結串列切斷
  • 遞迴分割時,輸入當前連結串列左端點head和中心節點slow.next(因為連結串列是從slow切斷的)
  • 終止條件:head.next = null時,說明只有一個節點了,直接返回此節點。

2.合併(merge):將兩個排序連結串列合併,轉化為一個排序連結串列。

該方法的時間複雜度為O(nlogn),空間複雜度因為使用了遞迴的方法所以應該為O(n)

 1 /**
 2  * Definition for singly-linked list.
 3  * public class ListNode {
 4  *     int val;
 5  *     ListNode next;
 6  *     ListNode() {}
 7  *     ListNode(int val) { this.val = val; }
 8  *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
9 * } 10 */ 11 class Solution { 12 public ListNode sortList(ListNode head) { 13 if(head == null || head.next == null) return head; 14 ListNode slow = head, fast = head.next; 15 while(fast != null && fast.next != null){ 16 slow = slow.next; 17 fast = fast.next.next;
18 } 19 ListNode temp = slow.next; 20 slow.next = null; 21 ListNode left = sortList(head); 22 ListNode right = sortList(temp); 23 ListNode h = new ListNode(); 24 ListNode res = h; 25 while(left != null && right != null){ 26 if(left.val <= right.val){ 27 res.next = left; 28 left = left.next; 29 }else{ 30 res.next = right; 31 right = right.next; 32 } 33 res = res.next; 34 } 35 res.next = left == null ? right : left; 36 return h.next; 37 } 38 }

方法二:自底向上歸併排序

使用自底向上的方法實現歸併排序,則可以達到 O(1)O(1) 的空間複雜度。

首先求得連結串列的長度 \textit{length}length,然後將連結串列拆分成子連結串列進行合併。

具體做法如下。

用 subLength 表示每次需要排序的子連結串列的長度,初始時 subLength=1。

每次將連結串列拆分成若干個長度為 subLength 的子連結串列(最後一個子連結串列的長度可以小於subLength),按照每兩個子連結串列一組進行合併,合併後即可得到若干個長度為 subLength×2 的有序子連結串列(最後一個子連結串列的長度可以小於 subLength×2)。合併兩個子連結串列仍然使用「21. 合併兩個有序連結串列」的做法。

將 subLength 的值加倍,重複第 2 步,對更長的有序子連結串列進行合併操作,直到有序子連結串列的長度大於或等於 length,整個連結串列排序完畢。

如何保證每次合併之後得到的子連結串列都是有序的呢?可以通過數學歸納法證明。

初始時 subLength=1,每個長度為 11 的子連結串列都是有序的。

如果每個長度為 subLength 的子連結串列已經有序,合併兩個長度為 subLength 的有序子連結串列,得到長度為 subLength×2 的子連結串列,一定也是有序的。

當最後一個子連結串列的長度小於 subLength 時,該子連結串列也是有序的,合併兩個有序子連結串列之後得到的子連結串列一定也是有序的。

因此可以保證最後得到的連結串列是有序的。

 1 class Solution {
 2     public ListNode sortList(ListNode head) {
 3         if (head == null) {
 4             return head;
 5         }
 6         int length = 0;
 7         ListNode node = head;
 8         while (node != null) {
 9             length++;
10             node = node.next;
11         }
12         ListNode dummyHead = new ListNode(0, head);
13         for (int subLength = 1; subLength < length; subLength <<= 1) {
14             ListNode prev = dummyHead, curr = dummyHead.next;
15             while (curr != null) {
16                 ListNode head1 = curr;
17                 for (int i = 1; i < subLength && curr.next != null; i++) {
18                     curr = curr.next;
19                 }
20                 ListNode head2 = curr.next;
21                 curr.next = null;
22                 curr = head2;
23                 for (int i = 1; i < subLength && curr != null && curr.next != null; i++) {
24                     curr = curr.next;
25                 }
26                 ListNode next = null;
27                 if (curr != null) {
28                     next = curr.next;
29                     curr.next = null;
30                 }
31                 ListNode merged = merge(head1, head2);
32                 prev.next = merged;
33                 while (prev.next != null) {
34                     prev = prev.next;
35                 }
36                 curr = next;
37             }
38         }
39         return dummyHead.next;
40     }
41 
42     public ListNode merge(ListNode head1, ListNode head2) {
43         ListNode dummyHead = new ListNode(0);
44         ListNode temp = dummyHead, temp1 = head1, temp2 = head2;
45         while (temp1 != null && temp2 != null) {
46             if (temp1.val <= temp2.val) {
47                 temp.next = temp1;
48                 temp1 = temp1.next;
49             } else {
50                 temp.next = temp2;
51                 temp2 = temp2.next;
52             }
53             temp = temp.next;
54         }
55         if (temp1 != null) {
56             temp.next = temp1;
57         } else if (temp2 != null) {
58             temp.next = temp2;
59         }
60         return dummyHead.next;
61     }
62 }