有趣的事,Python永远不会缺席!
一、归并排序(MERGE-SORT)概念
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。归并排序适用于子序列有序的数据排序。
1、原理
归并排序是分治法的典型应用。分治法(Divide-and-Conquer):将原问题划分成 n 个规模较小而结构与原问题相似的子问题;递归地解决这些问题,然后再合并其结果,就得到原问题的解。从上图看分解后的数列很像一个二叉树。
归并排序采用分而治之的原理:
将一个序列从中间位置分成两个序列;
在将这两个子序列按照第一步继续二分下去;
直到所有子序列的长度都为1,也就是不可以再二分截止。这时候再两两合并成一个有序序列即可。
原理如上图,图片来源于
2、举例
对以下数组进行归并排序:
[11, 99, 33 , 69, 77, 88, 55, 11, 33, 36,39, 66, 44, 22]
2. 首先,进行数组分组,即
[11, 99, 33 , 69, 77, 88, 55], [ 11, 33, 36,39, 66, 44, 22]
[11, 99, 33] , [69, 77, 88, 55], [ 11, 33, 36], [39, 66, 44, 22]
[11], [99, 33] , [69, 77], [88, 55],[ 11], [33, 36],[39, 66], [44, 22]
直到所有子序列的长度都为1,也就是不可以再二分截止。
[11], [99], [33] , [69], [77], [88], [55],[ 11], [33], [36],[39], [66], [44], [22]
3. 这时候再两两合并成一个有序序列即可。
[11],[33,99],[69,77],[55,88],[11],[33,36],[39,66],[22,44]
[11,33,99],[55,69,77,88],[11,33,36],[22,39,44,66]
[11,33,55,69,77,88,99],[11,22,33,36,39,44,66]
4、最终排序
1 [11, 11, 22, 33, 33, 36, 39, 44, 55, 66, 69, 77, 88, 99]
二、代码
代码用jupyternotebook实现
1 defmerge_sort(arr):2 """归并排序"""
3 if len(arr) == 1:4 returnarr5 #使用二分法将数列分两个
6 mid = len(arr) // 2
7 left =arr[:mid]8 right =arr[mid:]9 #使用递归运算
10 returnmarge(merge_sort(left), merge_sort(right))11
12
13 defmarge(left, right):14 """排序合并两个数列"""
15 result =[]16 #两个数列都有值
17 while len(left) > 0 and len(right) >0:18 #左右两个数列第一个最小放前面
19 if left[0] <=right[0]:20 result.append(left.pop(0))21 else:22 result.append(right.pop(0))23 #只有一个数列中还有值,直接添加
24 result +=left25 result +=right26 returnresult27
28 merge_sort([11, 99, 33 , 69, 77, 88, 55, 11, 33, 36,39, 66, 44, 22])29
30 #返回结果[11, 11, 22, 33, 33, 36, 39, 44, 55, 66, 69, 77, 88, 99]
这里记录一下,python有一个模块,专门提供了归并排序的方法,叫做“heapq”模块,因此我们只要将分解后的结果导入该方法即可。例如:
1 from heapq importmerge2
3 defmerge_sort(lst):4 if len(lst) <= 1:5 return lst #从递归中返回长度为1的序列
6 middle = len(lst) // 2
7 left = merge_sort(lst[:middle]) #通过不断递归,将原始序列拆分成n个小序列
8 right =merge_sort(lst[middle:])9 returnlist(merge(left, right))10
11
12 merge_sort([11, 99, 33 , 69, 77, 88, 55, 11, 33, 36,39, 66, 44, 22])13
14 #返回结果[11, 11, 22, 33, 33, 36, 39, 44, 55, 66, 69, 77, 88, 99]
三、特点
比较性:排序时元素之间需要比较,所以为比较排序
稳定性:我们从代码中可以看到当左边的元素小于等于右边的元素就把左边的排前面,而原本左边的就是在前面,所以相同元素的相对顺序不变,故为稳定排序
时间复杂度: 复杂度为O(nlog^n)
空间复杂度:在合并子列时需要申请临时空间,而且空间大小随数列的大小而变化,所以空间复杂度为O(n)
记忆方法:所谓归并肯定是要先分解,再合并
结果