手撕归并排序:原来这就是分治法的终极奥义!(附C语言源码)

当分糖果遇到算法设计

小时候分糖果的经历(记得吗?),总是先把整包糖分成两半,再继续对半分——这和今天要讲的归并排序核心思想不谋而合!这个看似简单的分治法(Divide and Conquer),在算法世界里可是扛把子级别的存在!

拆解归并排序四大核心步骤

第一步:暴力拆分(重要!)

把数组想象成一根法棍面包,从中间切开两半。比如数组[5,3,9,1,7,2],第一次拆分就变成:

左半部 → [5,3,9]
右半部 → [1,7,2]

(关键细节)不断递归拆分,直到每个子数组只剩1个元素。这时候每个单元素数组自然就是有序的!

第二步:有序合并(核心操作)

当拆到最小单元后,开始像拼积木一样合并。比如合并[3,5]和[1,9]:

  1. 创建临时数组
  2. 比较两数组首位 → 1 < 3 → 放入1
  3. 继续比较 → 3 < 7 → 放入3
  4. 重复直到所有元素归位
    最终得到[1,3,5,7,9]

第三步:双指针魔法

合并过程中需要两个指针:

int i = left;   // 左数组起点
int j = mid+1; // 右数组起点

通过这两个指针的移动,实现**O(n)**时间复杂度的合并操作!

第四步:临时数组的妙用

重点来了(敲黑板)!合并时必须使用临时数组存储中间结果,合并完成后再把数据拷贝回原数组。这个操作虽然增加了空间复杂度,但却是保证稳定性的关键!

手把手C语言实现

#include <stdio.h>
#include <stdlib.h>

void merge(int arr[], int left, int mid, int right) {
    int n1 = mid - left + 1;
    int n2 = right - mid;
    
    // 创建临时数组
    int L[n1], R[n2];
    
    // 拷贝数据
    for (int i = 0; i < n1; i++)
        L[i] = arr[left + i];
    for (int j = 0; j < n2; j++)
        R[j] = arr[mid + 1 + j];
    
    // 合并操作
    int i = 0, j = 0, k = left;
    while (i < n1 && j < n2) {
        if (L[i] <= R[j]) {
            arr[k] = L[i];
            i++;
        } else {
            arr[k] = R[j];
            j++;
        }
        k++;
    }
    
    // 处理剩余元素
    while (i < n1) {
        arr[k] = L[i];
        i++;
        k++;
    }
    while (j < n2) {
        arr[k] = R[j];
        j++;
        k++;
    }
}

void mergeSort(int arr[], int left, int right) {
    if (left < right) {
        int mid = left + (right - left) / 2; // 防止整型溢出
        
        // 递归拆分
        mergeSort(arr, left, mid);
        mergeSort(arr, mid + 1, right);
        
        // 合并
        merge(arr, left, mid, right);
    }
}

性能分析(重点!)

指标数值说明
时间复杂度O(n log n)最坏/平均情况都稳定(划重点!)
空间复杂度O(n)需要额外存储空间
稳定性稳定相同元素顺序不变

什么时候该用它?

  1. 处理大数据量文件排序(比如GB级日志文件)
  2. 需要稳定排序的场景(比如先按姓名排序再按年龄)
  3. 内存足够大的情况下(毕竟要额外空间)
  4. 链表排序的最佳选择(空间复杂度可降为O(1))

真实开发中的坑

去年处理用户行为日志时,用快排遇到最坏情况直接O(n²)超时(血泪教训!)。换成归并排序后,50GB日志文件20分钟搞定,真香!

结语

归并排序就像算法界的太极——看似缓慢拆分,实则暗藏合并杀招。下次面试被问排序算法,把这个分治思路甩出来,绝对让面试官眼前一亮!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值