分治法:归并排序
歸并排序的思想:
對(duì)集合的排序,可以看成他們子集合的分別排序,然后把各個(gè)有序的子集合合并成有序的全集,這里一般把原集合盡可能的均分為2個(gè)子集合。
可以看出這里有兩個(gè)工作量,一是把原數(shù)組不斷的對(duì)半切,直到子問題足夠小可以直接解出,二是把兩個(gè)有序的數(shù)組合并成一個(gè)有序的數(shù)組。
def MergeSort(arr,N):# 遞歸出口,出口需要返回當(dāng)前的那個(gè)數(shù)if N == 1:return arr# 以下當(dāng)作某一層的處理middle = N //2# 獲取待排序的兩個(gè)已排序的數(shù)組left_list = MergeSort(arr[:middle],len(arr[:middle]))right_list = MergeSort(arr[middle:],len(arr[middle:]))# 如下是針對(duì)兩個(gè)已排序的數(shù)組合并成一個(gè)有序數(shù)組的排列方法,為最基本的雙針模型i ,j =0,0result =[]while i < len(left_list) and j < len(right_list):if left_list[i] <= right_list[j]:result.append(left_list[i])i +=1else:result.append(right_list[j])j += 1result += left_list[i:]result += right_list[j:]# 返回已排序的結(jié)果,用于上一層獲取待排序的有序數(shù)組return result我們想一下可以發(fā)現(xiàn),對(duì)半切這個(gè)操作可以省略掉,對(duì)半切是為了切成大小為1的肉丁,但是數(shù)組本來就是已經(jīng)切好了的,那我們一個(gè)直接開始合并的操作,兩兩合并,四四合并,…
def MergeSort_iteration(arr):# 把一個(gè)數(shù)組arr[left:mid+1]和arr[mid+1:right+1]排成一個(gè)有序的序列,最后結(jié)果還是在# 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,0result =[]while i < len(left_list) and j < len(right_list):if left_list[i] <= right_list[j]:result.append(left_list[i])i +=1else:result.append(right_list[j])j += 1result += left_list[i:]result += right_list[j:] arr[left:right+1] = result[:]# 雙針模型自然合并排序# 最外面的大的指針,1,2,4,8,16,32...,直到大于len(arr) cur_size = 1# 終止條件為超過了數(shù)組長(zhǎng)度,前半部分為2的i次方,后半部分為2的i次方到endwhile cur_size < len(arr):# 內(nèi)部循環(huán),用于更新每一塊的順序,只要一個(gè)left指針就可以更新每一個(gè)局部left = 0# 截至條件同樣是left越界while left < len(arr)-1:# mid的位置,-1為數(shù)組是從0開始,簡(jiǎn)單分析一個(gè)實(shí)例就明白了mid = left + cur_size -1# right的位置,一般情況下是mid + cur_size,同樣不能越界,越界時(shí)區(qū)len(arr)-1right = (mid + cur_size,len(arr)-1)[mid + cur_size>len(arr)-1]# 把制定區(qū)域的數(shù)排列有序sorting_two_sorted_arr_in_place(arr,left,mid,right)# 更新下一塊,每一個(gè)固定cur_size循環(huán)里面的步長(zhǎng)為2倍cur_sizeleft += 2*cur_size# 更新外部循環(huán)的步長(zhǎng)cur_size *=2return arr運(yùn)行結(jié)果
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]我們還直到歸并排序的時(shí)間復(fù)雜度為O(nlogn),而且還是穩(wěn)定的,排序問題的時(shí)間下界就是nlogn,歸并是一個(gè)漸近最優(yōu)算法,為什么歸并排序是一個(gè)漸近最優(yōu)算法?這涉及到排序的本質(zhì),排序的本質(zhì)是消除逆序?qū)?#xff0c;假如逆序?qū)]有了,整個(gè)數(shù)組就已經(jīng)有序了,歸并排序先消除局部的逆序?qū)?#xff0c;再消除全局的逆序?qū)?#xff0c;這樣可以減少不必要的操作。為什么歸并是一個(gè)穩(wěn)定的算法,歸并排序先消除局部的逆序?qū)?#xff0c;再消除全局的逆序?qū)?#xff0c;這樣不會(huì)理論上造成不穩(wěn)定。
總結(jié)
- 上一篇: 分治:分治和动态规划的区别,二分检索递归
- 下一篇: 分治法:快速排序,3种划分方式,随机化快