归并排序(Merge Sort)
「归并排序 merge sort」是一种基于分治策略的排序算法:
【1】划分阶段:通过递归不断地将数组从中点处分开,将长数组的排序问题转换为短数组的排序问题。
【2】合并阶段:当子数组长度为 1 时终止划分,开始合并,持续地将左右两个较短的有序数组合并为一个较长的有序数组,直至结束。
算法流程
“划分阶段”从顶至底递归地将数组从中点切分为两个子数组
“合并阶段”从底至顶地将左子数组和右子数组合并为一个有序数组。需要注意的是,从长度为 1 的子数组开始合并,合并阶段中的每个子数组都是有序的。
【1】计算数组中点 mid ,递归划分左子数组(区间 [left, mid] )和右子数组(区间 [mid + 1, right] )
【2】递归执行步骤 【1】 ,直至子数组区间长度为 1 时,终止递归划分。
C++版本
#include <iostream>
using namespace std;
const int N = 1000010;
int n;
int q[N], tmp[N];
void merge_sort(int q[], int l, int r)
{
if(l >= r) return;
int mid = l + r >> 1;
merge_sort(q, l, mid);
merge_sort(q, mid+1, r);
int k = 0, i = l, j = mid + 1;
while(i <= mid && j <= r)
{
if(q[i] <= q[j]) tmp[k++] = q[i++];
else tmp[k++] = q[j++];
}
while(i <= mid) tmp[k++] = q[i++];
while(j <= r) tmp[k++] = q[j++];
for(i = l, j = 0; i <= r; i++, j++) q[i] = tmp[j];
}
int main()
{
scanf("%d", &n);
for(int i = 0; i < n; i++) scanf("%d", &q[i]);
merge_sort(q, 0, n-1);
for(int i = 0; i < n; i++) printf("%d ", q[i]);
return 0;
}
Python版本
def merge(nums: list[int], left: int, mid: int, right: int):
"""合并左子数组和右子数组"""
# 左子数组区间[left, mid]
# 右子数组区间[mid + 1, right]
# 初始化辅助数组
tmp = list(nums[left : right + 1])
# 左子数组的起始索引和结束索引
left_start = 0
left_end = mid - left
# 右子数组的起始索引和结束索引
right_start = mid + 1 - left
right_end = right - left
# i, j 分别指向左子数组、右子数组的首元素
i = left_start
j = right_start
# 通过覆盖原数组 nums 来合并左子数组和右子数组
for k in range(left, right + 1):
# 若“左子数组已全部合并完”,则选取左子数组元素,并且j++
if i > left_end:
nums[k] = tmp[j]
j += 1
# 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且i++
elif j > right_end or tmp[i] <= tmp[j]:
nums[k] = tmp[i]
i += 1
# 否则,若“左右子数组都未完全合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且j++
else:
nums[k] = tmp[j]
j += 1
def merge_sort(nums: list[int], left: int, right: int):
"""归并排序"""
# 终止条件
if left >= right:
return # 当子数组长度为 1 时终止递归
# 划分阶段
mid = (left + right) // 2 # 计算中点
merge_sort(nums, left, mid) # 递归左子数组
merge_sort(nums, mid + 1, right) # 递归右子数组
# 合并阶段
merge(nums, left, mid, right)