歸併排序的實現及其優化(遞迴法)

2021-08-28 19:47:22 字數 4766 閱讀 9242

歸併排序

一、思路(遞迴)

list = [a,b,c,d]

1、 遞迴過程

1、陣列一分為2,list1 = [a,b] list2 = [c,d]

2、先確立遞迴項:分別對list1/list2做歸併排序,此時可以假設左右子陣列已經有序。

3、執行merge子過程,將list1/list2合併並使之有序。

3、在程式首部新增基準條件:當前候選陣列長度為1,直接return,停止向下遞迴。

經典的歸併排序,目標陣列採用閉區間來標記,即[left,right],各標記定義如下:

陣列長度 n=7

index 0 1 2 3 4 5 6

value 3 2 5 | 7 1 3 6

標記 left mid right

注:n = 7

left = 0

right = n -1

mid = right // 2

2、merge子過程

1、函式傳入整個陣列,要merge的子陣列區間範圍[left,mid],[mid+1,right]

2、做完整陣列的切片 a=list[left,right+1]

3、設定內部指標i遍歷子陣列1,j遍歷子陣列2

指標k遍歷賦值給完整陣列

二、實現

from sort_helper import test_sort,random_array

def __merge(array, left, mid, right):

temp = array[left:right+1]

#新陣列temp的左中右標記,充當常量

n = len(temp)

l = 0

r = n-1

m = l + (r -l) // 2

#左陣列標記i,右陣列標記j,賦值外部迴圈標記k

i = 0

j = m + 1

#遍歷【0,n)即整個陣列長度,每次確定乙個下標應該賦值的元素並完成賦值操作

k = 0

while k < n:

#2、定義判斷條件,防越界;迴圈的次數是由n確定的,不存在i,j同時越界的情況

if i > m:

array[k + left] = temp[j]

j += 1

elif j > r:

array[k + left] = temp[i]

i += 1

#1、判斷兩個子陣列當前位置的值,並賦值給原陣列array的對應位置,此處可能涉及到越界問題,所以迴圈主體開始之前要判斷一下

elif temp[i] < temp[j]:

array[k + left] = temp[i]

i += 1

else:

array[k + left] = temp[j]

j += 1

k += 1

#錯誤示範

def __merge1(array, left, mid, right):

#錯誤1,開闢陣列空間只針對一部分元素,而非整個陣列

temp = array[:]

#錯誤2,left應該最後賦值為0,否則後兩部相當於減0,無作用

#錯誤3:內部端點就應該重新定義為l、r、mid;一旦否則array[k+left]無效果,因為left已變..

left = 0

mid = mid - left

right = right - left

n = right - left + 1

i = 0

j = mid + 1

k = 0

while k < n:

if i > mid:

array[k + left] = temp[j]

j += 1

elif j > right:

array[k + left] = temp[i]

i += 1

elif temp[i] < temp[j]:

array[k + left] = temp[i]

i += 1

else:

array[k + left] = temp[j]

j += 1

k += 1

print(array)

#該私有函式需要左右下標

def __merge_sort(array, left, right):

#3、新增基準條件,左右區間短點,相差1即可,即子陣列長度為1

if right -left <= 0:

return

#1、一分為二

mid = left + (right -left) // 2

#2、左右分別歸併排序,應該考慮到要新增基準條件了,否則無限遞迴

__merge_sort(array, left, mid)

__merge_sort(array, mid+1, right)

#4、此時,array兩個子陣列都已有序,但整個陣列卻還無序,需要合併

__merge(array, left, mid, right)

def merge_sort(array):

#作為帶外暴露的歸併排序函式,不需要左右下標

__merge_sort(array, 0, len(array)-1)

if __name__ == '__main__':

array = random_array()

test_sort('merge_sort', merge_sort, array)

三、優化

1、提前終止不必要的merge操作

def __merge_sort1(array, left, right):

#3、新增基準條件,左右區間短點,相差1即可,即子陣列長度為1

if right -left <= 0:

return

#1、一分為二

mid = left + (right -left) // 2

#2、左右分別歸併排序,應該考慮到要新增基準條件了,否則無限遞迴

__merge_sort1(array, left, mid)

__merge_sort1(array, mid+1, right)

#4、此時,array兩個子陣列都已有序,但整個陣列卻還無序,需要合併

if array[mid] > array[mid+1]:

__merge(array, left, mid, right)

def merge_sort1(array):

#作為帶外暴露的歸併排序函式,不需要左右下標

__merge_sort1(array, 0, len(array)-1)

2、使用插入排序優化
def __merge_sort2(array, left, right):

if right -left <= 50:

insert_sort(array, left, right)

#必須要有return,否則無限遞迴

return

mid = left + (right -left) // 2

__merge_sort2(array, left, mid)

__merge_sort2(array, mid+1, right)

if array[mid] > array[mid+1]:

__merge(array, left, mid, right)

def merge_sort2(array):

__merge_sort2(array, 0, len(array)-1)

3、測試用例
if __name__ == '__main__':

優化效果在有序性較強的情況下比較明顯

歸併排序(合併排序) 遞迴法

參考了一些大佬的 再自己總結了一下。原理基本不變。歸併排序 合併排序 是一種分治演算法。這個演算法不斷地將乙個陣列分為兩部分,分別對左子陣列和右子陣列排序,然後將兩個陣列合併為新的有序陣列。穩定 是 時間複雜度 最優 o nlog n 最差 o nlog n 平均 o nlog n include ...

歸併排序演算法 遞迴法

首先乙個問題,如何將兩個整數進行公升序排序?這不簡單嗎。將兩個數比較,再將小的放在前面,大的放在後面。然後如何將兩個公升序的陣列排序為乙個陣列呢?我知道我知道 建立第三個陣列,將需要那兩個陣列按下標順序進行比較,然後將小的數放入第三個陣列中,在將兩者下標加一。如,將a陣列與b陣列的第乙個元素比較,假...

歸併排序遞迴實現

include include define length 10 using namespace std void merge int ar,int br,int start,int mid,int end else if i mid else void copy int ar,int br,int...