本文描述了LeetCode 148題 sort-list 的解法。html
題目描述以下:
Sort a linked list in O(n log n) time using constant space complexity.java
題目要求咱們在O(n log n)時間複雜度下完成對單鏈表的排序,咱們知道平均時間複雜度爲O(n log n)的排序方法有快速排序、歸併排序和堆排序。而通常是用數組來實現二叉堆,固然能夠用二叉樹來實現,可是這麼作太麻煩,還得花費額外的空間構建二叉樹,因而不採用堆排序。
故本文采用快速排序和歸併排序來對單鏈表進行排序。node
在通常實現的快速排序中,咱們經過首尾指針來對元素進行切分,下面採用快排的另外一種方法來對元素進行切分。git
咱們只須要兩個指針p1和p2,這兩個指針均往next方向移動,移動的過程當中保持p1以前的key都小於選定的key,p1和p2之間的key都大於選定的key,那麼當p2走到末尾時交換p1與key值便完成了一次切分。github
圖示以下:
數組
代碼以下:ui
public ListNode sortList(ListNode head) { //採用快速排序 quickSort(head, null); return head; } public static void quickSort(ListNode head, ListNode end) { if (head != end) { ListNode node = partion(head, end); quickSort(head, node); quickSort(node.next, end); } } public static ListNode partion(ListNode head, ListNode end) { ListNode p1 = head, p2 = head.next; //走到末尾才停 while (p2 != end) { //大於key值時,p1向前走一步,交換p1與p2的值 if (p2.val < head.val) { p1 = p1.next; int temp = p1.val; p1.val = p2.val; p2.val = temp; } p2 = p2.next; } //當有序時,不交換p1和key值 if (p1 != head) { int temp = p1.val; p1.val = head.val; head.val = temp; } return p1; }
歸併排序應該算是鏈表排序最佳的選擇了,保證了最好和最壞時間複雜度都是nlogn,並且它在數組排序中廣受詬病的空間複雜度在鏈表排序中也從O(n)降到了O(1)。spa
歸併排序的通常步驟爲:指針
首先用快慢指針(快慢指針思路,快指針一次走兩步,慢指針一次走一步,快指針在鏈表末尾時,慢指針剛好在鏈表中點)的方法找到鏈表中間節點,而後遞歸的對兩個子鏈表排序,把兩個排好序的子鏈表合併成一條有序的鏈表。code
代碼以下:
public ListNode sortList(ListNode head) { //採用歸併排序 if (head == null || head.next == null) { return head; } //獲取中間結點 ListNode mid = getMid(head); ListNode right = mid.next; mid.next = null; //合併 return mergeSort(sortList(head), sortList(right)); } /** * 獲取鏈表的中間結點,偶數時取中間第一個 * * @param head * @return */ private ListNode getMid(ListNode head) { if (head == null || head.next == null) { return head; } //快慢指針 ListNode slow = head, quick = head; //快2步,慢一步 while (quick.next != null && quick.next.next != null) { slow = slow.next; quick = quick.next.next; } return slow; } /** * * 歸併兩個有序的鏈表 * * @param head1 * @param head2 * @return */ private ListNode mergeSort(ListNode head1, ListNode head2) { ListNode p1 = head1, p2 = head2, head; //獲得頭節點的指向 if (head1.val < head2.val) { head = head1; p1 = p1.next; } else { head = head2; p2 = p2.next; } ListNode p = head; //比較鏈表中的值 while (p1 != null && p2 != null) { if (p1.val <= p2.val) { p.next = p1; p1 = p1.next; p = p.next; } else { p.next = p2; p2 = p2.next; p = p.next; } } //第二條鏈表空了 if (p1 != null) { p.next = p1; } //第一條鏈表空了 if (p2 != null) { p.next = p2; } return head; }
完整代碼放在:
https://github.com/morethink/algorithm/blob/master/src/main/java/algorithm/leetcode/L_148_SortList.java
參考文檔: