归并排序算法详解
1. 算法概述
归并排序(Merge Sort)是一种基于分治法(Divide and Conquer)的高效排序算法。该算法采用递归思想,将待排序的序列不断拆分成更小的子序列,直到每个子序列只有一个元素,然后将这些有序的子序列合并成更大的有序序列,最终完成整个序列的排序。
核心特点
- 稳定性:归并排序是稳定的排序算法
- 时间复杂度:始终为 O(n log n)
- 空间复杂度:O(n),需要额外的存储空间
- 适用场景:适合大数据量的排序,特别是链表排序
2. 算法原理与步骤
2.1 分治策略
归并排序的核心思想包含三个主要步骤:
| 步骤 | 描述 | 作用 |
|---|---|---|
| 分解 | 将序列递归地分成两半 | 将大问题转化为小问题 |
| 解决 | 对子序列进行排序 | 递归处理子问题 |
| 合并 | 将有序子序列合并 | 组合子问题的解 |
2.2 详细执行流程
python
def merge_sort(arr):
"""
归并排序主函数
"""
if len(arr) <= 1:
return arr
# 分解步骤:找到中间位置
mid = len(arr) // 2
# 递归处理左右子序列
left = merge_sort(arr[:mid])
right = merge_sort(arr[mid:])
# 合并步骤:合并两个有序子序列
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
3. 动图演示原理
虽然无法直接展示动态图像,但可以通过文字描述归并排序的动图演示过程:
初始序列 :[38, 27, 43, 3, 9, 82, 10]
分解过程:
第一次分解:[38, 27, 43, 3] 和 [9, 82, 10]
第二次分解:[38, 27] 和 [43, 3] | [9, 82] 和 [10]
第三次分解:[38] 和 [27] | [43] 和 [3] | [9] 和 [82] | [10]
合并过程:
第一层合并:[27, 38] 和 [3, 43] | [9, 82] 和 [10]
第二层合并:[3, 27, 38, 43] 和 [9, 10, 82]
最终结果:[3, 9, 10, 27, 38, 43, 82]
这个过程展示了归并排序如何通过递归分解和有序合并来实现排序。
4. 完整Python实现
4.1 基础版本
python
def merge_sort_complete(arr):
"""
完整的归并排序实现(原地修改)
"""
if len(arr) > 1:
# 分解步骤
mid = len(arr) // 2
left_arr = arr[:mid]
right_arr = arr[mid:]
# 递归排序
merge_sort_complete(left_arr)
merge_sort_complete(right_arr)
# 合并步骤
i = j = k = 0
# 比较合并
while i < len(left_arr) and j < len(right_arr):
if left_arr[i] <= right_arr[j]:
arr[k] = left_arr[i]
i += 1
else:
arr[k] = right_arr[j]
j += 1
k += 1
# 处理剩余元素
while i < len(left_arr):
arr[k] = left_arr[i]
i += 1
k += 1
while j < len(right_arr):
arr[k] = right_arr[j]
j += 1
k += 1
# 测试示例
test_array = [64, 34, 25, 12, 22, 11, 90]
print("原始数组:", test_array)
merge_sort_complete(test_array)
print("排序后数组:", test_array)
4.2 优化版本(减少空间占用)
python
def merge_sort_optimized(arr, left=0, right=None, temp=None):
"""
优化版归并排序,减少空间占用
"""
if right is None:
right = len(arr) - 1
if temp is None:
temp = [0] * len(arr)
if left < right:
mid = (left + right) // 2
# 递归排序左右部分
merge_sort_optimized(arr, left, mid, temp)
merge_sort_optimized(arr, mid + 1, right, temp)
# 合并有序序列
merge_optimized(arr, left, mid, right, temp)
def merge_optimized(arr, left, mid, right, temp):
"""
优化合并函数
"""
i, j, k = left, mid + 1, left
# 比较合并
while i <= mid and j <= right:
if arr[i] <= arr[j]:
temp[k] = arr[i]
i += 1
else:
temp[k] = arr[j]
j += 1
k += 1
# 复制剩余元素
while i <= mid:
temp[k] = arr[i]
i += 1
k += 1
while j <= right:
temp[k] = arr[j]
j += 1
k += 1
# 将临时数组复制回原数组
for idx in range(left, right + 1):
arr[idx] = temp[idx]
5. 算法复杂度分析
5.1 时间复杂度
| 情况 | 时间复杂度 | 说明 |
|---|---|---|
| 最好情况 | O(n log n) | 序列已经有序 |
| 平均情况 | O(n log n) | 随机序列 |
| 最坏情况 | O(n log n) | 序列完全逆序 |
推导过程:
- 分解深度:log₂n 层
- 每层合并操作:O(n)
- 总时间复杂度:O(n log n)
5.2 空间复杂度
- 基础版本:O(n log n) - 递归调用栈和临时数组
- 优化版本:O(n) - 只使用一个临时数组
6. 实际应用场景
6.1 适用场景
- 大数据排序:当数据量很大且内存充足时
- 外部排序:处理无法全部装入内存的大文件
- 链表排序:归并排序特别适合链表数据结构
- 稳定排序需求:需要保持相等元素相对位置时
6.2 不适用场景
- 小数据量:插入排序可能更高效
- 内存受限:快速排序的空间效率更高
- 几乎有序数据:冒泡排序或插入排序更好
7. 与其他排序算法对比
| 特性 | 归并排序 | 快速排序 | 堆排序 |
|---|---|---|---|
| 时间复杂度 | O(n log n) | O(n log n) | O(n log n) |
| 稳定性 | 稳定 | 不稳定 | 不稳定 |
| 空间复杂度 | O(n) | O(log n) | O(1) |
| 适用数据 | 大数据量 | 通用 | 大数据量 |
8. 扩展应用
8.1 逆序对计数
python
def count_inversions(arr):
"""
使用归并排序思想计算逆序对数量
"""
if len(arr) <= 1:
return arr, 0
mid = len(arr) // 2
left, inv_left = count_inversions(arr[:mid])
right, inv_right = count_inversions(arr[mid:])
merged, inv_merge = merge_count(left, right)
return merged, inv_left + inv_right + inv_merge
def merge_count(left, right):
"""
合并并计数逆序对
"""
result = []
i = j = 0
inversions = 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])
inversions += len(left) - i # 关键步骤:计数逆序对
j += 1
result.extend(left[i:])
result.extend(right[j:])
return result, inversions
8.2 外部排序实现
归并排序是外部排序的基础,常用于处理超大规模数据集的排序问题。其基本思想是将大数据集分成多个小块,在内存中排序后写回磁盘,然后使用多路归并的方法合并这些有序块。
归并排序以其稳定的性能和可预测的时间复杂度,在需要可靠排序性能的场景中得到了广泛应用。虽然它的空间复杂度相对较高,但其稳定性和对大数据量的良好适应性使其成为重要的排序算法之一。