news 2026/2/26 1:06:46

归并排序的趟数和时间复杂度

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
归并排序的趟数和时间复杂度

一、归并排序的趟数

归并排序的核心是分治思想:先把数组递归地分成两半(分),直到每个子数组只有 1 个元素;再把相邻的子数组合并成有序数组(治)。这里的 “趟数”,本质是合并阶段的轮次(也等于拆分阶段的递归深度)。

1. 趟数的计算逻辑

假设待排序数组的元素个数为n

  • 拆分阶段:每次将数组分成 2 份,直到子数组长度为 1。这个过程的次数等于以 2 为底的 n 的对数(向上取整或向下取整,最终结果一致),数学表示为⌊log₂n⌋⌈log₂n⌉(也可简化为log₂n,因为时间复杂度中对数的底数不影响阶数)。
  • 合并阶段:每一轮合并都是将当前的子数组两两合并,轮次和拆分的次数完全相同,这就是归并排序的趟数
2. 举例说明
  • 例 1:n=8(2³)拆分过程:8 → 4+4 → 2+2+2+2 → 1+1+…+1(共 8 个 1)。拆分次数(趟数):log₂8=3趟。
  • 例 2:n=7(非 2 的幂)拆分过程:7 → 3+4 → 1+2+2+2 → 1+1+1+1+1+1+1。拆分次数(趟数):log₂7≈2.8,向上取整为3 趟(或向下取整⌊log₂7⌋=2,但实际合并时仍需 3 趟,因此通常统一表述为log₂n趟,忽略小数部分的影响)。

结论:归并排序的趟数为 **log2​n**(n为元素个数,对数的底数为 2)。

二、归并排序的时间复杂度

归并排序的时间复杂度需要从拆分阶段合并阶段分别分析,最终结合得出整体复杂度。

1. 拆分阶段的时间复杂度

拆分操作只是将数组分成两半,没有元素的比较和交换,仅涉及索引的计算,时间复杂度为O(1)(每一次拆分),整个拆分阶段的总时间复杂度为 O (log n)(因为拆分次数是 log n 趟),相对于合并阶段可以忽略。

2. 合并阶段的时间复杂度

合并阶段是归并排序的核心,每一趟合并都需要遍历所有 n 个元素(将两两子数组合并时,每个元素都会被处理一次)。

  • 每一趟合并的时间复杂度:O (n)。
  • 总趟数:log n。
  • 因此,合并阶段的总时间复杂度:O (n × log n)。
3. 不同情况下的时间复杂度

归并排序是稳定的排序算法(相同值的元素相对位置不变),且其时间复杂度不受原始数组有序性的影响:

  • 最好情况:O (n log n)(原始数组已有序)。
  • 最坏情况:O (n log n)(原始数组逆序)。
  • 平均情况:O (n log n)。
补充:空间复杂度

归并排序需要额外的辅助数组来存储合并后的元素,因此空间复杂度为O(n)(这是归并排序的主要缺点,相比快速排序的 O (log n) 空间复杂度更高)。

三、代码示例(验证趟数和复杂度)

下面是 Python 实现的归并排序,添加了趟数统计的功能,帮助你直观看到趟数:

python

运行

def merge_sort(arr): # 统计趟数(使用非局部变量,避免全局变量) global merge_pass # 递归终止条件:子数组长度为1 if len(arr) <= 1: return arr # 拆分:将数组分成左右两半 mid = len(arr) // 2 left = merge_sort(arr[:mid]) right = merge_sort(arr[mid:]) # 合并:将两个有序子数组合并 merge_pass += 1 # 每合并一次,趟数+1(这里统计的是合并操作的次数,总趟数为log2n) return merge(left, right) def merge(left, right): result = [] i = j = 0 # 双指针遍历两个子数组,按序添加到结果 while i < len(left) and j < len(right): if left[i] <= right[j]: result.append(left[i]) i += 1 else: result.append(right[j]) j += 1 # 添加剩余元素 result.extend(left[i:]) result.extend(right[j:]) return result # 测试 if __name__ == "__main__": # 初始化趟数 merge_pass = 0 arr = [3, 1, 4, 1, 5, 9, 2, 6] sorted_arr = merge_sort(arr) print("排序后的数组:", sorted_arr) print("合并操作的次数(趟数相关):", merge_pass) # 对于n=8,输出7(合并次数),趟数为3(log28=3)

说明:代码中merge_pass统计的是合并操作的次数(比如 n=8 时,合并次数是 7),而我们说的 “趟数” 是合并的轮次(n=8 时是 3 轮),两者的关系是:第 k 趟的合并次数为n/2k,总合并次数为n−1(比如 8-1=7)。

总结

  1. 趟数:归并排序的趟数为 **log2​n**(n 为元素个数,即拆分 / 合并的轮次),若 n 不是 2 的幂,趟数为向上取整后的log2​n。
  2. 时间复杂度:最好、最坏、平均情况均为O(n log n),这是归并排序的核心优势(不受原始数组有序性影响)。
  3. 补充:归并排序的空间复杂度为O(n),因为需要辅助数组存储合并后的元素。

合并阶段是归并排序的核心,每一趟合并都需要遍历所有 n 个元素 讲讲

要理解 “归并排序的每一趟合并都需要遍历所有 n 个元素”,我们需要先明确 “” 的定义(合并阶段的 “轮次”),再结合具体例子拆解每一趟的合并过程 —— 核心逻辑是:每一趟合并的目标是将当前所有 “有序子数组” 两两合并,而这些子数组的总长度恰好是 n,因此遍历的总元素数必然是 n

第一步:先明确 “合并阶段的趟” 是什么

归并排序的 “合并阶段”,是从 “最小有序子数组”(长度为 1 的子数组,天然有序)开始,每一轮(一趟)都将所有相邻的 “同长度有序子数组” 两两合并成 “长度翻倍的有序子数组”,直到最终合并成一个长度为 n 的有序数组。

这里的 “趟”=“合并轮次”,每一趟的核心特征是:

  • 趟内所有待合并的子数组长度相同(记为len);
  • 合并后生成的子数组长度为2*len
  • 每一趟处理的子数组总数是 “当前子数组个数”,但它们的总长度始终是 n。

第二步:用具体例子拆解 “每趟遍历 n 个元素”

n=8(元素:[3,1,4,1,5,9,2,6])为例,合并阶段共 3 趟(因为log₂8=3),我们逐趟分析:

第 1 趟合并:子数组长度 = 1 → 合并后长度 = 2
  • 初始状态:拆分阶段结束后,得到 8 个长度为 1 的有序子数组(每个子数组只有 1 个元素,天然有序):[3], [1], [4], [1], [5], [9], [2], [6]
  • 合并逻辑:将相邻的两个子数组两两合并,共需要合并 4 次(8 个→4 个):
    1. 合并[3][1][1,3](遍历 2 个元素);
    2. 合并[4][1][1,4](遍历 2 个元素);
    3. 合并[5][9][5,9](遍历 2 个元素);
    4. 合并[2][6][2,6](遍历 2 个元素);
  • 第 1 趟总遍历元素数:2+2+2+2 = 8 = n;
  • 第 1 趟结束后:得到 4 个长度为 2 的有序子数组:[1,3], [1,4], [5,9], [2,6]
第 2 趟合并:子数组长度 = 2 → 合并后长度 = 4
  • 当前状态:4 个长度为 2 的有序子数组,总长度仍为 8;
  • 合并逻辑:继续相邻两两合并,共需要合并 2 次(4 个→2 个):
    1. 合并[1,3][1,4]
      • 双指针遍历两个子数组,依次取较小元素:1(来自第二个子数组)→1(第一个)→3(第一个)→4(第二个),最终得到[1,1,3,4](遍历 4 个元素);
    2. 合并[5,9][2,6]
      • 双指针遍历:2(第二个)→5(第一个)→6(第二个)→9(第一个),最终得到[2,5,6,9](遍历 4 个元素);
  • 第 2 趟总遍历元素数:4+4 = 8 = n;
  • 第 2 趟结束后:得到 2 个长度为 4 的有序子数组:[1,1,3,4], [2,5,6,9]
第 3 趟合并:子数组长度 = 4 → 合并后长度 = 8(最终数组)
  • 当前状态:2 个长度为 4 的有序子数组,总长度还是 8;
  • 合并逻辑:最后一次两两合并(2 个→1 个):合并[1,1,3,4][2,5,6,9]
    • 双指针遍历:1(第一个)→1(第一个)→2(第二个)→3(第一个)→4(第一个)→5(第二个)→6(第二个)→9(第二个),最终得到[1,1,2,3,4,5,6,9](遍历 8 个元素);
  • 第 3 趟总遍历元素数:8 = n;
  • 第 3 趟结束后:得到 1 个长度为 n 的有序数组,排序完成。

第三步:为什么 “每趟都必须遍历 n 个元素”?

核心原因有两个,结合上面的例子很容易理解:

  1. 子数组总长度不变:无论每趟的子数组长度是 1、2 还是 4,所有子数组的总长度始终是 n(因为合并只是 “组合子数组”,没有增加或减少元素);
  2. 合并需完整遍历子数组:要将两个有序子数组合并成一个有序数组,必须完整遍历这两个子数组的所有元素(否则无法保证合并后的数组有序)。因此,每趟合并时,所有子数组的元素都会被遍历一次,总遍历数就是 n。

第四步:延伸思考 ——n 不是 2 的幂时,是否仍满足 “每趟遍历 n 个元素”?

即使 n 不是 2 的幂(比如 n=7),结论依然成立。以 n=7 为例,合并阶段共 3 趟(log₂7≈2.8,向上取整为 3 趟):

  • 第 1 趟:7 个长度为 1 的子数组 → 合并 3 次(得到 3 个长度为 2 的子数组 + 1 个长度为 1 的子数组),总遍历元素数 = 2+2+2+1=7=n;
  • 第 2 趟:3 个长度为 2 的子数组 + 1 个长度为 1 的子数组 → 合并 2 次(得到 1 个长度为 4 的子数组 + 1 个长度为 2 的子数组 + 1 个长度为 1 的子数组?不,实际是 “优先合并同长度”,最终总遍历数仍为 7);
  • 第 3 趟:合并剩余子数组,总遍历数还是 7=n。

本质是:即使子数组长度不完全相同,每趟所有待合并子数组的总长度依然是 n,因此遍历的总元素数始终是 n

总结

“归并排序每一趟合并都遍历所有 n 个元素” 的核心逻辑可概括为:

  • 合并的本质是 “将当前所有有序子数组合并成更大的有序子数组”;
  • 所有子数组的总长度永远等于 n(元素没有增减);
  • 合并任意两个子数组都需要完整遍历它们的元素,因此每趟遍历的总元素数必然是 n。

这也是归并排序时间复杂度为 O (n log n) 的关键:共 log n 趟,每趟 O (n),总复杂度 O (n log n)。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/21 11:36:06

14、Unix系统信息查询与环境配置全解析

Unix系统信息查询与环境配置全解析 在Unix系统的日常使用中,我们常常需要了解系统的运行状态、其他登录用户的信息,同时也可能需要对自己的环境变量进行配置和管理。下面就来详细介绍相关的操作方法。 一、查看其他登录用户信息 在Unix系统中,我们可以使用 w 命令来查看…

作者头像 李华
网站建设 2026/2/24 13:14:42

24、Unix 网络连接追踪、域名匹配与新闻阅读及文件编码指南

Unix 网络连接追踪、域名匹配与新闻阅读及文件编码指南 1. 使用 traceroute 追踪连接 当我们连接到远程计算机时,实际上是通过一系列的计算机(包括路由器和其他网络设备)来完成的。数据以数据包的形式在网络路径中传输,并在目的地重新组合成正确的顺序。不过,并非所有数…

作者头像 李华
网站建设 2026/2/25 21:30:40

30、Unix实用技巧:编码、备份与重定向

Unix实用技巧:编码、备份与重定向 1. ROT13编码与sed的使用 在一些场景中,文本常使用ROT13编码。ROT13即“rotate (the alphabet by) 13”的缩写,也就是将字母表旋转13位,例如A变成N,B变成O等。使用ROT13编码可以让不想看到某些内容的人避免看到,比如包含冒犯性笑话或电…

作者头像 李华
网站建设 2026/2/24 11:31:39

20、Awk 函数全面解析

Awk 函数全面解析 1. 函数概述 函数是一种自包含的计算单元,它接受若干参数作为输入,并返回某个值。Awk 具有两类内置函数:算术函数和字符串函数,同时也支持用户自定义函数,这使得我们能够通过编写自己的函数来扩展内置函数的功能。 2. 算术函数 Awk 中有九个内置函数…

作者头像 李华
网站建设 2026/2/17 9:11:26

24、Awk编程:数值限制、调用语法及不同版本特性解析

Awk编程:数值限制、调用语法及不同版本特性解析 1. Awk数值限制与脚本问题 在数值处理方面,Awk使用双精度浮点数,其大小受机器架构限制。在开发搜索程序时,可能会遇到输入记录过长的问题。例如,曾有一个搜索程序,它将文档按段落读取,若字段包含搜索词则打印该段落,可…

作者头像 李华
网站建设 2026/2/22 16:17:12

28、脚本游戏与云存储脚本实用指南

脚本游戏与云存储脚本实用指南 1. Acey Deucey 脚本游戏 Acey Deucey 是一个有趣的纸牌游戏脚本,下面我们来详细了解它。 1.1 游戏代码片段 if [ $splitValue -eq 0 ] ; thenecho "No point in betting when theyre the same rank!"continue fi /bin/echo -n &…

作者头像 李华