归并排序(Merging Sort)算法是一种稳定排序算法,和堆排序算法一样,都是利用完全二叉树的深度是 ⌊ l o g n ⌋ \lfloor logn\rfloor ⌊logn⌋+1 的特性,来提高排序效率,其时间复杂度和堆排序相同,均为O(nlogn)
。
归并排序就是利用归并(将两个或以上的有序表组合成一个新的有序表)的思想实现的排序方法。
基本原理是:假设初设序列有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1, 然后两两归并,得到 ⌈ n / 2 ⌉ \lceil n/2 \rceil ⌈n/2⌉( ⌈ x ⌉ \lceil x \rceil ⌈x⌉表示不小于x的最小整数)个长度为2或1的有序子序列;再两两归并,……,如此重复,直至得到一个长度为n的有序序列为止,这种方法被称为2路归并排序。
以下面的数组为例,对其使用归并排序算法进行升序排序,整个流程如下面的树状图所示(由于Markdown语法会自动调整子树的位置,因此子树的左右顺序不完全正确),开始递归后,将数组逐渐拆分,直至子序列只有一个元素,然后对有序子序列进行归并,最终合并为一个有序的数组。
901050803070406020
def mergeList(li1, li2):
# 合并两个有序数组
li = []
i = 0
j = 0
while i < len(li1) or j < len(li2):
if j >= len(li2) or (i < len(li1) and li1[i] <= li2[j]):
li.append(li1[i])
i += 1
else:
li.append(li2[j])
j += 1
return li
def mergeRecursion(li):
"""
递归方式实现归并排序,稳定排序
时间复杂度O(n*logn)
"""
if len(li) <= 1:
return li
# 将数组划分为左右两部分
mid = (len(li)) // 2
li1 = mergeRecursion(li[:mid])
li2 = mergeRecursion(li[mid:])
return mergeList(li1, li2)
if __name__ == '__main__':
li = [90, 10, 50, 80, 30, 70, 40, 60, 20]
print('归并排序(递归法):', mergeRecursion(li.copy()))
由于上述代码大量使用的递归,代码结构非常清晰,容易理解,但也会造成时间和空间上的性能损耗。通过将递归方式改造为迭代方式,避免了递归时log2n的栈空间,空间复杂度为O(n),避免递归在时间性能上也有一定的提升。
因此使用归并排序时,应尽量使用非递归方式。
下面代码中用到的mergeList
函数,与上述代码中的相同。
def mergePass(li, k):
i = 0
lo = []
while i < len(li): # 避免剩余len(li)-x*k(不足k个)元素不参与排序,因此设置条件为只要i
lo.extend(mergeList(li[i:i+k], li[i+k:i+2*k]))
i = i + 2 * k
return lo
def mergeIteration(li):
"""
迭代方式实现归并排序,性能高于递归方式
"""
k = 1
n = len(li)
while k < n:
li = mergePass(li, k)
k *= 2
return li
if __name__ == '__main__':
li = [90, 10, 50, 80, 30, 70, 40, 60, 20]
print('归并排序(迭代法):', mergeIteration(li.copy()))
以上,欢迎指正交流~
参考资料:
[1] 《大话数据结构》
[1] 排序算法(一)——冒泡排序算法详解及Python实现
[2] 排序算法(二)——简单选择排序算法详解及Python实现
[3] 排序算法(三)——直接插入排序算法详解及Python实现
[4] 排序算法(四)——希尔排序算法详解及Python实现
[5] 排序算法(五)——堆排序算法详解及Python实现
[6] 排序算法(六)——归并排序算法详解及Python实现
[7] 排序算法(七)——快速排序算法详解及Python实现