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 }