一、歸並排序
-
所謂“分”,指的是將一個亂序數列不斷進行二分,得到許多短的序列。
-
所謂“治”,指的是將這些短序列進行兩兩合並,然后將合並的結果作為新的序列,再與其他序列進行合並,最終得到一個新的序列。
歸並排序算法描述
-
把長度為n的輸入序列分成兩個長度為n/2的子序列;
-
對這兩個子序列分別采用歸並排序;
-
將兩個排序好的子序列合並成一個最終的排序序列。
歸並排序動圖演示
動畫演示圖2
歸並排序代碼實現
def merge_sort(alist): """歸並排序""" n = len(alist) #遞歸結束條件 # 剩一個或沒有直接返回,不用排序 if n <= 1: return alist # 拆分 mid = n//2 # left 采用歸並排序后形成的有序的新的列表 left_li = merge_sort(alist[:mid]) # right 采用歸並排序后形成的有序的新的列表 right_li = merge_sort(alist[mid:]) # 將兩個有序的子序列合並為一個新的整體 # merge(left, right) left_pointer, right_pointer = 0, 0 result = [] while left_pointer < len(left_li) and right_pointer < len(right_li): if left_li[left_pointer] <= right_li[right_pointer]: result.append(left_li[left_pointer]) left_pointer += 1 else: result.append(right_li[right_pointer]) right_pointer += 1 #將兩個列表按順序融合為一個列表result result += left_li[left_pointer:] result += right_li[right_pointer:] return result
歸並排序過程分析
示例 針對 arrli = [6,5,3,1,8,7,2,4]進行歸並排序
1、拆分數組
假設數組一共有 n 個元素,我們遞歸對數組進行折半拆分即n//2
,直到每組只有一個元素為止。
2、合並數組
算法會從最小數組開始有序合並,這樣合並出來的數組一直是有序的,所以合並兩個有序數組是歸並算法的核心,這里用兩個簡單數組示例:
步驟1:新建一個空數組存放合並結果,用left_pointer
和right_pointer
兩個輔助指針記錄兩個數組當前操作位置;
步驟2:從左到右逐一比較兩個小數組中的元素,較小的元素先放入新數組,指針移位,直到left_pointer
和right_pointer
指針超出尾部;
步驟3:新建一個空數組存放合並結果,用l
和r
兩個輔助指針記錄兩個數組當前操作位置;
步驟4:從左到右逐一比較兩個小數組中的元素,較小的元素先放入新數組,指針移位,直到l
或r
指針超出尾部;
繼續比較寫入較小的元素到新數組
繼續比較寫入較小的元素到新數組
指針尚未移到尾部的數組,說明還有剩余元素,將剩余元素合並到新數組尾部。
步驟5:新建一個空數組存放合並結果,用l
和r
兩個輔助指針記錄兩個數組當前操作位置;
步驟6:從左到右逐一比較兩個小數組中的元素,較小的元素先放入新數組,指針移位,直到l
或r
指針超出尾部;
將較小的元素寫入到新數組
繼續比較寫入較小的元素到新數組
繼續比較寫入較小的元素到新數組
繼續比較寫入較小的元素到新數組
步驟7:右邊的指針尚未移到尾部的數組,說明還有剩余元素,將剩余元素合並到新數組尾部。
完成歸並排序,返回排好序的新數組
歸並排序復雜度
-
時間復雜度:O(nlogn)
歸並排序把數組一層層折半分組,長度為 n 的數組,折半層數就是 logn,每一層進行操作的運算量是 n,得出時間復雜度 O(nlogn)。
-
空間復雜度:O(n)
每次歸並操作需要創建額外的新數組,占用空間為 n,但這部分額外空間會隨着方法的結束而釋放,所以只需要算單次歸並操作開辟的空間即可,得出空間復雜度 O(n)。
-
穩定性:穩定
從算法中從左到右逐一比較,較小的先放入新數組,所以兩個值相同的元素,排序后依然保持原先后順序。