Python实现顺序表的归并排序

1,242 阅读3分钟

1 归并排序原理

1.1 概述

  • 归并排序(Merge sort)是建立在归并操作上的一种有效、稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
  • 基本思路大致可以概括为先通过递归的方式将给定的数组二分到仅有一个元素,再通过两两大小比较进行排序,最后逐级合并完成总体的排序。
  • 归并排序的效率较高,设数列长为 n,将数列分开成小数列一共要 logn 步,每步都是一个合并有序数列的过程,时间复杂度为 O(nlogn),也就是O(n)。

1.2 举个栗子

实例:将顺序表[4,8,2,3,5,9,1,4,0,7]从小到大排序

  • 按照递归的思想,首先要实现“递”的过程,也就是把给定的数组逐级向下二分,如下图所示:

屏幕截图 2021-07-29 221313.png

  • 当数组分解为两个或一个元素的时候,就可以进行比较和排序了,排序后逐层向上合并也就是“归”的过程,这一过程也是算法的重点:

屏幕截图 2021-07-29 222613.png

  • 两两元素比较排序的过程较为简单,例如[8,4]=>[4,8]
  • 再往上一层的比较合并就稍微复杂一些:以图中右侧的[5,9] [0,1,4]=>[0,1,4,5,9]来具体说明:
    • 首先将两个数组中的第一个元素进行比较,较小的元素就可以填入上层数组的第一个位置。两个数组的第一个元素分别为0和5,0<5,因此0就成功晋升,填入上级数组的第一个位置。
    • 然后将0所在的数组中的第二个元素与5进行比较。第二个元素是1,1<5,1也成功晋升,填入上级数组的第二个位置。
    • 这里要注意的是,假设0所在的数组中的第二个元素不是1而是7,那么7>5,那么晋升的就是5了,随后则需要把5后面的元素和7来比大小。
    • 以此类推,直至一方的元素都晋升了,则把另一方剩下的元素原封不动地放到上级数组的最后。
  • 按此规则逐级合并,那么到最顶上时,数组就会变为有序数组了,排序工作也就大功告成了~

2 算法实现

2.1 算法步骤

通过案例,已经对算法的执行逻辑有所了解,现在将算法步骤抽象化:

  • “递”的步骤:
  1. 将数组从中间二分
  2. 将二分后的两个数组分别再次二分
  3. 重复步骤2直到数组不可分
  • “归”的步骤
  1. 创建一个空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置
  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
  4. 重复步骤3直到某一指针达到序列尾
  5. 将另一序列剩下的所有元素直接复制到合并序列尾

2.2 Python代码

# 数组合并
def conquer(left,right):
    res = []    # 创建空列表
    l,r = 0,0   # 设定两个指针来指向列表中的元素,从第一个元素开始
    # 当任意一方列表中的元素都填入res时,循环停止
    while l < len(left) and r < len(right): 
        # 较小的元素放入res,指针后移一位
        if left[l] < right[r]:
            res.append(left[l])
            l = l + 1
        else:
            res.append(right[r])
            r = r + 1
    # 将剩余的元素放在res的尾部
    res = res + left[l:]
    res = res + right[r:]
    return res
# 归并排序
def merge_sort(list):
    # 设置递归的退出条件
    if len(list) <= 1:
        return list
    # 设置二分点
    split = len(list)//2
    # 将数组向下逐级二分
    left = merge_sort(list[:split])
    right = merge_sort(list[split:])
    # 向上逐级排序合并
    return conquer(left,right)

来运行一下看看最终的成果:

a = [4,8,2,3,5,9,1,4,0]
merge_sort(a)

>
[0, 1, 2, 3, 4, 4, 5, 8, 9]