算法-归并排序

归并排序 (Merge Sort)

像"分家再合并"一样,先把队伍从中间劈成两半,一直劈到每个人自成一个小队(天然有序)。然后两两合并,在合并时像"双指针"一样,每次只比较两个小队排头的人,谁小谁先站到新的队伍里。不断向上合并,最后整个队伍就排好序了。

核心代码

c 复制代码
#include <stdio.h>

// 辅助数组,用于在合并时暂存数据
int temp[100]; 

// 合并函数:将两个已排序的子数组合并成一个有序数组
void merge(int arr[], int left, int mid, int right) {
    int i = left;      // 左半部分起始指针
    int j = mid + 1;   // 右半部分起始指针
    int k = left;      // 辅助数组的填充指针
    
    // 双指针比较:哪边小,就把哪边的元素放入 temp
    while (i <= mid && j <= right) {
        if (arr[i] <= arr[j]) {
            temp[k++] = arr[i++];
        } else {
            temp[k++] = arr[j++];
        }
    }
    
    // 将左半部分剩余的元素直接倒入 temp
    while (i <= mid) temp[k++] = arr[i++];
    // 将右半部分剩余的元素直接倒入 temp
    while (j <= right) temp[k++] = arr[j++];
    
    // 将排好序的 temp 数据倒回原数组 arr
    for (int p = left; p <= right; p++) {
        arr[p] = temp[p];
    }
}

// 归并排序函数
void mergeSort(int arr[], int left, int right) {
    if (left >= right) return; // 递归终止条件:区间只有一个元素
    
    int mid = (left + right) / 2; // 找到中间分界点
    mergeSort(arr, left, mid);    // 递归排序左半边
    mergeSort(arr, mid + 1, right); // 递归排序右半边
    merge(arr, left, mid, right); // 合并左右两边
}

// 打印数组的辅助函数
void printArray(int arr[], int n) {
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

int main() {
    int arr[] = {64, 34, 25, 12, 22};
    int n = sizeof(arr) / sizeof(arr[0]); // 计算数组长度
    
    printf("排序前:");
    printArray(arr, n);
    
    mergeSort(arr, 0, n - 1); // 调用归并排序
    
    printf("排序后:");
    printArray(arr, n);
    return 0;
}

示例演示

{64, 34, 25, 12, 22}

第 1 步:不断递归拆分(分治的"分")

目标:将数组从中间劈开,直到每个子数组只剩 1 个元素。

拆分 {64, 34, 25, 12, 22} -> {64, 34} 和 {25, 12, 22}

继续拆分 {64, 34} -> {64} 和 {34}

继续拆分 {25, 12, 22} -> {25} 和 {12, 22}

继续拆分 {12, 22} -> {12} 和 {22}

拆分结束:得到 5 个单元素子数组,它们天然有序。
第 2 步:自底向上合并(分治的"治")

目标:利用双指针,将相邻的有序子数组合并成更大的有序数组。

合并 {64} 和 {34}:比较 64 > 34,先放 34,再放 64。结果:{34, 64}

合并 {12} 和 {22}:比较 12 < 22,先放 12,再放 22。结果:{12, 22}

合并 {25} 和 {12, 22}:比较 25 > 12,放 12;比较 25 > 22,放 22;左半部分剩 25 直接放入。结果:{12, 22, 25}

当前状态:数组变成了两个有序块:34, 6412, 22, 25
第 3 步:最终合并

目标:将最后两个有序块合并成完整的有序数组。

合并 {34, 64} 和 {12, 22, 25}:

比较 34 > 12,放 12;比较 34 > 22,放 22;比较 34 > 25,放 25。

此时右半边已合并完,将左半边剩余的 {34, 64} 直接追加到末尾。

合并完成。

最终结果:12, 22, 25, 34, 64

复杂度

时间复杂度是 \(O(nlogn)\)(无论最好、最坏还是平均情况,拆分和合并的代价都非常稳定)

空间复杂度是 \(O(n)\)(因为合并时需要借助一个与原数组等长的辅助数组 temp 来暂存数据)

相关推荐
wuqingshun3141591 小时前
说一下消息队列的模型有哪些?
java
GIS阵地2 小时前
QgsRasterDataProvider 完整详解(QGIS 3.40.13 C++)
开发语言·c++·qt·开源软件·qgis
fīɡЙtīиɡ ℡2 小时前
布隆过滤器
java
yaoxin5211233 小时前
462. Java 反射 - 获取声明类与封闭类
java·开发语言·python
阿里嘎多学长3 小时前
2026-07-14 GitHub 热点项目精选
开发语言·程序员·github·代码托管
charlie1145141914 小时前
Cinux: 为大内核铺路
开发语言·c++·操作系统·现代c++
2501_914245934 小时前
C语言设计模式详解:从理论到实践的完整指南
c语言·开发语言·设计模式
Hazenix5 小时前
Go 指南:一篇文章速通 Golang
开发语言·后端·golang
灯澜忆梦5 小时前
GO_复合类型---指针
开发语言·后端·golang