歸併排序(leetcode148)
阿新 • • 發佈:2020-12-05
148. 排序連結串列
給你連結串列的頭結點 head
,請將其按 升序 排列並返回 排序後的連結串列 。
進階:
- 你可以在
O(n log n)
時間複雜度和常數級空間複雜度下,對連結串列進行排序嗎?
示例 1:
輸入:head = [4,2,1,3]
輸出:[1,2,3,4]
示例 2:
輸入:head = [-1,5,3,4,0]
輸出:[-1,0,3,4,5]
提示:
- 連結串列中節點的數目在範圍
[0, 5 * 104]
內 -105 <= Node.val <= 105
題解
思路
看到時間複雜度的要求,而且是連結串列,歸併排序比較好做。
都知道歸併排序要先歸(二分),再並。兩個有序的連結串列是才比較容易合併的。
二分到不能再二分,即遞迴壓棧到了底部,鏈只有一個結點,本身就是有序的,於是在遞迴出棧時進行合併。
// 虛擬碼
func sortList (head) {
對連結串列進行二分
l = sortList(左鏈) // 已排序的左鏈
r = sortList(右鏈) // 已排序的右鏈
merged = mergeList(l, r) // 進行合併
return merged // 返回合併的結果給父呼叫
}
二分、merge 函式
-
二分,用快慢指標法,快指標走兩步,慢指標走一步,快指標越界時,慢指標正好到達中點,只需記錄慢指標的前一個指標,就能斷成兩鏈。
-
merge 函式做的事是合併兩個有序的左右鏈
-
設定虛擬頭結點,用 prev 指標去“穿針引線”,prev 初始指向 dummy
-
每次都確定 prev.Next 的指向,並注意 l1 / l2指標的推進,和 prev 指標的推進
-
最後返回 dummy.Next ,即合併後的鏈。
-
程式碼實現(golang)
節點的定義:
//Definition for singly-linked list.
type ListNode struct {
Val int
Next *ListNode
}
MERGESORT:
// 把無序陣列變為有序陣列 func sortList(head *ListNode) *ListNode { if head == nil || head.Next == nil { // 遞迴的出口,不用排序 直接返回 return head } slow, fast := head, head // 快慢指標 var preSlow *ListNode // 儲存slow的前一個結點 for fast != nil && fast.Next != nil { preSlow = slow slow = slow.Next // 慢指標走一步 fast = fast.Next.Next // 快指標走兩步 } preSlow.Next = nil // 斷開,分成兩鏈 // 分治,把左邊的無序陣列變為有序陣列 l := sortList(head) // 已排序的左鏈 // 分治,把右邊的邊的無序陣列變為有序陣列 r := sortList(slow) // 已排序的右鏈 return mergeList(l, r) // 合併已排序的左右鏈,一層層向上返回 }
MERGE
兩種寫法:
-
迴圈實現分治
// 非遞迴寫法 func mergeList(l1, l2 *ListNode) *ListNode { dummy := &ListNode{Val: 0} // 虛擬頭結點 prev := dummy // 用prev去掃,先指向dummy for l1 != nil && l2 != nil { // l1 l2 都存在 if l1.Val < l2.Val { // l1值較小 prev.Next = l1 // prev.Next指向l1 l1 = l1.Next // 考察l1的下一個結點 } else { prev.Next = l2 l2 = l2.Next } prev = prev.Next // prev.Next確定了,prev指標推進 } if l1 != nil { // l1存在,l2不存在,讓prev.Next指向l1 prev.Next = l1 } if l2 != nil { prev.Next = l2 } return dummy.Next // 真實頭結點 }
-
遞迴實現分治
// 遞迴寫法,l1,l2均為有序陣列,將兩個有序數組合併為1個有序陣列 func mergeList(l1, l2 *ListNode) *ListNode { if l1==nil { return l2 } if l2==nil { return l1 } if l1.Val > l2.Val{ l2.Next = mergeList(l1, l2.Next) return l2 } l1.Next = mergeList(l2, l1.Next) return l1 }
MERGE和MERGESORT的演算法描述
MERGESORT(A)
- if len(A) <= 1 return A
- q \(\leftarrow\) \(\frac{len(A)}{2}\)
- L \(\leftarrow\) A[0...q], R \(\leftarrow\) A[q + 1...len(A) - 1]
- L \(\leftarrow\) MERGESORT(L)
- R \(\leftarrow\) MERGESORT(R)
- return MERGE(L,R)
MERGE(L,R)
- i \(\leftarrow\) 0; j \(\leftarrow\) 0; aux \(\leftarrow\) [ ]
- while i < len(L) and j < len(R)
- if L[i] \(\leq\) R[j]
- aux:add(L[i]) ; i \(\leftarrow\) i + 1
- else aux:add(R[j]) ; j \(\leftarrow\) j + 1
- if i == len(L)
- aux.add(R[j : len(R) - 1])
- else aux.add(L[i : len(L) - 1])
- return