归并排序(Merge Sort)

本文详细介绍了归并排序的基本思想、实现逻辑、时间复杂度和空间复杂度分析。归并排序是一种稳定的排序方法,通过递归或非递归的方式将序列不断分割并合并,最终达到完全有序。时间复杂度为O(nlog⁡2(n)),空间复杂度为O(n)。同时,提供了递归和非递归两种Python实现方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

归并排序(Merge Sort)

一、基本思想

归并排序是一种稳定的排序方法。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。

二、实现逻辑

对于一个待排序的数组,首先进行分解,将整个待排序数组以 m i d mid mid 中间位置为界,一分为二,随后接着分割,直至最小单位无法分割;开始进行“治”的操作,将每两个小部分进行排序,并逐步合并;直至合并成整个数组的大小。

算法步骤:

  1. 将一个序列从中间位置分成两个序列;
  2. 在将这两个子序列按照第一步继续二分下去;
  3. 直到所有子序列的长度都为1,不可以再二分截止,再两两合并成一个有序序列。

三、时间复杂度的分析

归并排序的时间复杂度是 O ( n log ⁡ 2 ( n ) ) O(n \log_2(n)) O(nlog2(n)),则这个时间复杂度是稳定的,不随需要排序的序列的不同而产生波动。假设我们需要对一个包含 n n n 个数的序列使用归并排序,并且使用的是递归的实现方式,那么过程如下:

  • 递归的第一层,将 n n n 个数划分为 2 2 2 个子区间,每个子区间的数字个数为 n / 2 n/2 n/2
  • 递归的第二层,将 n n n 个数划分为 4 4 4 个子区间,每个子区间的数字个数为 n / 4 n/4 n/4
  • 递归的第三层,将 n n n 个数划分为 8 8 8 个区间,每个子区间的数字个数为 n / 8 n/8 n/8
  • 递归的第 log ⁡ 2 ( n ) \log_2(n) log2(n) 层,将 n n n 个数划分为 n n n 个子区间,每个子区间的数字个数为1;

归并排序的过程中,需要对当前区间进行对半划分,直到区间的长度为1。也就是说,每一层的子区间,长度都是上一层的 1 / 2 1/2 1/2。这也就意味着,当划分到第 log ⁡ 2 ( n ) \log_2(n) log2(n)的时候,子区间的长度就是1了。而归并的“治”操作,则是从最底层开始(子区间为1的层),对相邻的两个子区间进行合并,过程如下:

  • 在第 log ⁡ 2 ( n ) \log_2(n) log2(n) 层(最底层),每个子区间的长度为1,共 n n n 个子区间,每相邻两个子区间进行合并,总共合并 n / 2 n/2 n/2次。 n n n 个数字都会被遍历一遍,所以这一层的总时间复杂度为 O ( n ) O(n) O(n)
  • 在第二层,每个子区间长度为 n / 4 n/4 n/4,总共有 4 4 4 子区间,每相邻两个子区间进行合并,总共合并 2 2 2 次。 n n n 个数字都会被遍历一次,所以这一层的总时间复杂度为 O ( n ) O(n) O(n)
  • 在第一层,每个子区间长度为 n / 2 n/2 n/2,总共有 2 2 2 个子区间,只需要合并一次。 n n n 个数字都会被遍历一次,所以这一层的总时间复杂度为 O ( n ) O(n) O(n)

通过上面的操作可以发现,对于每一层来说,在合并所有子区间的过程中, n n n 个元素都会被操作一次,所以每一层的时间复杂度都是 O ( n ) O(n) O(n)。归并排序化分子区间,将子区间划分为只剩 1 1 1 个元素,需要化分 log ⁡ 2 ( n ) \log_2(n) log2(n) 次。每一层的时间复杂度为 O ( n ) O(n) O(n),共有 log ⁡ 2 ( n ) \log_2(n) log2(n) 层,所以归并排序的时间复杂度是 Ω ( n log ⁡ 2 ( n ) ) \Omega(n \log_2(n)) Ω(nlog2(n)) Θ ( n log ⁡ 2 ( n ) ) \Theta(n \log_2(n)) Θ(nlog2(n)) O ( n log ⁡ 2 ( n ) ) O(n \log_2(n)) O(nlog2(n))

四、空间复杂度的分析

这是因为归并排序的合并函数,在合并两个有序数组为一个有序数组时,需要借助额外的存储空间。如果我们继续按照分析递归时间复杂度的方法,通过递推公式来求解,那整个归并过程需要的空间复杂度就是 O ( n log ⁡ 2 ( n ) ) O(n \log_2(n)) O(nlog2(n))。实际上,递归代码的空间复杂度并不能像时间复杂度那样累加。最重要的一点,尽管每次合并操作都需要申请额外的内存空间,但在合并完成之后,临时开辟的内存空间就被释放掉了。在任意时刻,CPU 只会有一个函数在执行,也就只会有一个临时的内存空间在使用。临时内存空间最大也不会超过 n n n 个数据的大小,所以空间复杂度是 O ( n ) O(n) O(n)

五、算法实现

递归实现

def merge_sort(array: list, reverse: bool=False) -> list:
    '''
    array: 支持数值型数据,如整型与浮点型混合;支持全为字符串类型的数据;不支持字符串型与数值型混合。
    reverse: 是否降序, 默认采用升序。
    '''
    if len(array) <= 1:
        return array
    mid = len(array) // 2
    left = merge_sort(array[:mid])
    right = merge_sort(array[mid:])
    return merge(left, right, reverse=reverse)

def merge(l: int, r: int, reverse: bool=False) -> list:
    '''
    l: 数据左侧游标(整型), r: 数据右侧游标(整型)
    '''
    result = []
    i = 0
    j = 0
    while i < len(l) and j < len(r):
        if (l[i] > r[j] if reverse else l[i] <= r[j]):
            result.append(l[i])
            i += 1
        else:
            result.append(r[j])
            j += 1
    result += l[i:]
    result += r[j:]
    return result

非递归实现

def merge(array: list, low: int, mid: int, high: int, reverse: bool=False) -> None:
    '''
    low: 数据低侧游标(整型), mid: 数据中间游标(整型), high: 数据高侧游标(整型)
    '''
    left = array[low: mid]
    right = array[mid: high]
    i = 0
    j = 0
    result = []
    while i < len(left) and j < len(right):
        if (left[i] > right[j] if reverse else left[i] <= right[j]):
            result.append(left[i])
            i += 1
        else:
            result.append(right[j])
            j += 1
    result += left[i:]
    result += right[j:]
    array[low: high] = result

def merge_sort(array: list, reverse: bool=False) -> None:
    '''
    array: 支持数值型数据,如整型与浮点型混合;支持全为字符串类型的数据;不支持字符串型与数值型混合。
    reverse: 是否降序, 默认采用升序。
    '''
    i = 1
    while i < len(array):
        low = 0
        while low < len(array):
            mid = low + i
            high = min(low + 2 * i, len(array))
            if mid < high:
                merge(array, low, mid, high, reverse=reverse)
            low += 2 * i
        i *= 2
### 归并排序的实现原理 归并排序是一种基于分治法(Divide and Conquer)的有效排序算法。其核心思想是通过将待排序数组分割为更小的部分,分别对这些部分进行排序后再将其合并成为一个整体有序的结果[^1]。 具体来说,归并排序的过程可以分为以下几个方面: #### 1. **分解** 整个数据集被递归地划分为较小的子集合,直到每个子集合仅包含单个元素为止。因为单一元素本身已经是有序的,所以这一步骤完成了基础单元的创建[^2]。 #### 2. **合并** 当所有的子集合都已经被拆解到最小单位之后,开始逐步地把它们两两合并起来,在每次合并的过程中都会确保新形成的组合也是按照顺序排列好的。这种“归并”的过程会一直持续下去,直至最终形成一个完整的、完全有序的数据列表[^3]。 以下是归并排序的核心伪代码表示: ```python def merge_sort(arr): if len(arr) <= 1: return arr mid = len(arr) // 2 left_half = merge_sort(arr[:mid]) right_half = merge_sort(arr[mid:]) return merge(left_half, right_half) def merge(left, right): sorted_array = [] i = j = 0 while i < len(left) and j < len(right): if left[i] < right[j]: sorted_array.append(left[i]) i += 1 else: sorted_array.append(right[j]) j += 1 sorted_array.extend(left[i:]) sorted_array.extend(right[j:]) return sorted_array ``` 这段代码展示了如何使用 Python 来实现归并排序的功能。其中 `merge` 函数负责执行实际的合并操作,而 `merge_sort` 则控制着递归调用以及何时停止进一步划分输入数组。 ### 时间复杂度分析 由于每一次都将当前序列分成两半处理,并且每一层都需要遍历全部 n 项来进行比较和移动,因此总的运行时间为 O(n log n)。 ### 稳定性特点 值得注意的是,归并排序属于稳定性的排序方式之一,这意味着即使存在相等的关键字记录也不会改变彼此原有的次序关系。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DeeGLMath

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值