分治法:歸併排序
阿新 • • 發佈:2018-12-17
歸併排序的思想:
對集合的排序,可以看成他們子集合的分別排序,然後把各個有序的子集合合併成有序的全集,這裡一般把原集合儘可能的均分為2個子集合。
可以看出這裡有兩個工作量,一是把原陣列不斷的對半切,直到子問題足夠小可以直接解出,二是把兩個有序的數組合併成一個有序的陣列。
def MergeSort(arr,N):
# 遞迴出口,出口需要返回當前的那個數
if N == 1:
return arr
# 以下當作某一層的處理
middle = N //2
# 獲取待排序的兩個已排序的陣列
left_list = MergeSort(arr[:middle],len(arr[:middle]))
right_list = MergeSort(arr[middle:],len(arr[middle:]))
# 如下是針對兩個已排序的數組合併成一個有序陣列的排列方法,為最基本的雙針模型
i ,j =0,0
result =[]
while i < len(left_list) and j < len(right_list):
if left_list[i] <= right_list[j]:
result. append(left_list[i])
i +=1
else:
result.append(right_list[j])
j += 1
result += left_list[i:]
result += right_list[j:]
# 返回已排序的結果,用於上一層獲取待排序的有序陣列
return result
我們想一下可以發現,對半切這個操作可以省略掉,對半切是為了切成大小為1的肉丁,但是陣列本來就是已經切好了的,那我們一個直接開始合併的操作,兩兩合併,四四合並,…
def MergeSort_iteration(arr):
# 把一個數組arr[left:mid+1]和arr[mid+1:right+1]排成一個有序的序列,最後結果還是在
# arr裡
def sorting_two_sorted_arr_in_place(arr,left,mid,right):
left_list = arr[left:mid+1]
right_list = arr[mid+1:right+1]
i ,j =0,0
result =[]
while i < len(left_list) and j < len(right_list):
if left_list[i] <= right_list[j]:
result.append(left_list[i])
i +=1
else:
result.append(right_list[j])
j += 1
result += left_list[i:]
result += right_list[j:]
arr[left:right+1] = result[:]
# 雙針模型自然合併排序
# 最外面的大的指標,1,2,4,8,16,32...,直到大於len(arr)
cur_size = 1
# 終止條件為超過了陣列長度,前半部分為2的i次方,後半部分為2的i次方到end
while cur_size < len(arr):
# 內部迴圈,用於更新每一塊的順序,只要一個left指標就可以更新每一個區域性
left = 0
# 截至條件同樣是left越界
while left < len(arr)-1:
# mid的位置,-1為陣列是從0開始,簡單分析一個例項就明白了
mid = left + cur_size -1
# right的位置,一般情況下是mid + cur_size,同樣不能越界,越界時區len(arr)-1
right = (mid + cur_size,len(arr)-1)[mid + cur_size>len(arr)-1]
# 把制定區域的數排列有序
sorting_two_sorted_arr_in_place(arr,left,mid,right)
# 更新下一塊,每一個固定cur_size迴圈裡面的步長為2倍cur_size
left += 2*cur_size
# 更新外部迴圈的步長
cur_size *=2
return arr
執行結果
arr = [7,3,66,33,22,66,99,0,1]
print(arr)
print(MergeSort(arr,len(arr)))
print(MergeSort_iteration(arr))
[7, 3, 66, 33, 22, 66, 99, 0, 1]
[0, 1, 3, 7, 22, 33, 66, 66, 99]
[0, 1, 3, 7, 22, 33, 66, 66, 99]