148.排序鏈表
阿新 • • 發佈:2018-12-09
sin code 二叉樹 color == 只有一個 pan init 問題
題目描述:
在 O(n log n) 時間復雜度和常數級空間復雜度下,對鏈表進行排序。
示例 1:
輸入: 4->2->1->3 輸出: 1->2->3->4
示例 2:
輸入: -1->5->3->4->0 輸出: -1->0->3->4->5
思路:
要求 O(n log n) 時間復雜度和常數級空間復雜度,首先想到的就是快速排序和歸並排序。
此處使用歸並排序,
歸並排序大致思想是: 1.將鏈表劃分左右兩部分--> 2.對左右鏈表進行排序 --> 3.將左右兩個有序鏈表進行合並
如果三個步驟如果分開實現的,都比較容易,但有人容易在第二步驟懵逼———為什麽劃分後,對左右鏈表進行排序,再合並,這不是多此一舉麽。直接使用第二步對原鏈表進行排序不就好了?
所以,在歸並排序中,實現第二步的實現,其實是靠第三部來完成的。
當兩個鏈表長度都為1時,即兩個鏈表只有一個結點,那麽我們就認為這兩個鏈表是有序的,直接進行 步驟3——將兩個有序鏈表進行合並,於是兩個鏈表合並成了一個長度為2的有序鏈表。
於是問題轉化為,如何把一個長鏈表 劃分為 長度為1的短鏈表? 很簡單,遞歸劃分,不斷的將長鏈表進行二分,最終分成多個長度為1的鏈表,然後進行合並, 過程類似於二叉樹。
感覺和快速排序的分治思想沒有太大區別。
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { val = x; } * } */ class Solution { public ListNode sortList(ListNode head) { return head == null ? head : merge(head); } //劃分 public ListNode merge(ListNode head){if (head.next == null) { //遞歸退出條件:即鏈表被劃分後只剩一個結點 return head; } ListNode fast = head, slow = head, pre = head; //這裏使用快慢指針 進行鏈表劃分 while (fast != null && fast.next != null) { pre = slow; slow = slow.next; fast = fast.next.next; } pre.next= null; //劃分結果是,fast為原鏈表尾.next, slow為劃分後鏈表右半部分表頭,pre為左半部分表尾結點,所以pre.next要為null ListNode l = merge(head); //左半部分遞歸劃分 ListNode r = merge(slow); //右半部分遞歸劃分 return mergeList(l,r); //合並鏈表 } //合並 此處f是合並兩個有序鏈表。 public ListNode mergeList(ListNode h1,ListNode h2){ ListNode dummy = new ListNode(0); ListNode head = dummy; while (h1 != null && h2 != null) { if (h1.val < h2.val) { head.next = h1; head = head.next; h1 = h1.next; }else { head.next = h2; head = head.next; h2 = h2.next; } } if (h1 == null) { head.next = h2; }else { head.next = h1; } return dummy.next; } }
148.排序鏈表