news 2026/5/1 20:10:24

归并排序:分治法的经典应用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
归并排序:分治法的经典应用

一、前言

归并排序是基于分治法的典型排序算法,通过递归将数组拆分为最小单元(单个元素),再通过合并操作将有序子序列逐步组合成完整有序序列。其核心在于分解与合并的协同操作


二、分治法与递归拆分

分治法将原问题分解为若干规模较小但结构相似的子问题,递归求解后再合并子问题的解。归并排序是分治法的直接体现:

  • 拆分:将待排序数组从中间分成左右两半,分别递归排序,直到每个子数组只包含一个元素(单个元素天然有序)。

  • 合并:将两个有序子数组合并为一个有序数组。

整个过程需要两个核心函数Divide(递归拆分)和Merge(合并两个有序数组)


三、函数设计

Divide函数负责递归拆分数组:

void Divide(int arr[], int left, int right) { int mid = (left + right) / 2; //可能会超int类型存储上限 (left/2 + right/2) if (left < right) { //[left, right] mid //左半边[left, mid] 右半边[mid + 1, right] Divide(arr, left, mid); Divide(arr, mid + 1, right); Merge(arr, left, mid, right); //合并 } }

Merge函数负责合并两个有序子数组:

void Merge(int arr[], int left, int mid, int right) { //0.安全判断 //1.申请一个辅助空间brr,长度只要能放下合并的这两个组的所有元素即可 int len = right - left + 1; int* brr = (int*)malloc(sizeof(int) * len); if (brr == NULL) return; //exit(EXIT_FAILURE); //2.申请两个变量i j, 分别指向两个组的第一个元素 int i = left; //左边组开头 int j = mid + 1; //右边组开头 int k = 0; //辅助数组下标 //3.进入while循环,循环条件i,j没有越界(循环合法) while (i <= mid && j <= right) { //4.比较i j指向的两个组的各自第一个元素值 // 谁小谁向下放(brr,相等的话也是下放) if (arr[i] <= arr[j]) brr[k++] = arr[i++]; else brr[k++] = arr[j++]; } //5.当while循环进不去,代表肯定i或者j走完了 //(有一个组的数据被挪动完了),则将另一个组没有挪动完的值同步 while (i <= mid) brr[k++] = arr[i++]; while (j <= right) brr[k++] = arr[j++]; //6.这是两个组的合并结果就在brr里面,最后再将brr的数据同步给arr for (k = 0; k < len; k++) { arr[left + k] = brr[k]; } // 释放临时空间 free(brr); brr = NULL; }

MergeSort函数对外接口

void MergeSort(int arr[], int len) { //执行递归分函数 Divide(arr, 0, len - 1); }

四、手算模拟

以数组[5, 3, 8, 4, 2]为例:

  1. 拆分阶段

    • 第一层拆分:[5,3,8][4,2]
    • 第二层拆分:[5,3][8][4][2]
    • 第三层拆分:[5][3]
  2. 合并阶段

    • 合并[5][3]得到[3,5]
    • 合并[3,5][8]得到[3,5,8]
    • 合并[4][2]得到[2,4]
    • 最终合并[3,5,8][2,4]得到[2,3,4,5,8]

五、复杂度分析

  • 时间复杂度:所有情况均为O(n log n)。每次递归将问题规模减半(log n层),每层需O(n)时间合并。
  • 空间复杂度:O(n)用于辅助数组。
  • 稳定性:稳定(由于合并时对相等元素优先取左子数组元素,算法是稳定的)

六、优缺点对比

  • 优点
    • 时间复杂度稳定为O(n log n),不受输入数据分布影响。
    • 稳定排序,适合需要保持原始顺序的场景。
  • 缺点
    • 需要额外O(n)空间,不适合内存严格受限的环境。
  • 对比O(n²)排序(如冒泡排序):
    • 归并排序在大规模数据下效率显著更高,但小规模数据可能因递归开销反而更慢。

下次我们将讲解另一种分治排序——快速排序,它采用不同的拆分策略(按基准元素划分),且可以做到原地排序。敬请期待

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

LLM生成式优化的核心挑战与设计策略

1. LLM迭代生成优化的核心挑战解析大型语言模型(LLMs)的生成式优化正在重塑自动化系统设计的范式。这种技术允许我们通过执行反馈来迭代改进各类数字工件——从代码片段到完整的工作流程&#xff0c;再到提示模板。想象一下&#xff0c;你正在训练一个新员工&#xff1a;初始阶…

作者头像 李华
网站建设 2026/5/1 19:54:58

AppImageLauncher终极指南:三步实现Linux桌面高效集成

AppImageLauncher终极指南&#xff1a;三步实现Linux桌面高效集成 【免费下载链接】AppImageLauncher Helper application for Linux distributions serving as a kind of "entry point" for running and integrating AppImages 项目地址: https://gitcode.com/gh_…

作者头像 李华
网站建设 2026/5/1 19:53:28

长期使用 Taotoken 服务后对计费透明度与账单可追溯性的实际感受

长期使用 Taotoken 服务后对计费透明度与账单可追溯性的实际感受 1. 账单明细的实用价值 在持续使用 Taotoken 服务数月后&#xff0c;最直接的感受是其账单明细对日常开发与成本管理带来的便利。每月初收到的账单会按模型类型、调用时间、项目标识等多个维度详细列出 token …

作者头像 李华
网站建设 2026/5/1 19:52:29

SwiftUI API请求的加密之旅

引言 在开发iOS应用时,API请求是与服务器进行数据交互的关键桥梁。然而,当我们遇到服务器返回500错误时,问题可能不仅仅在于代码的逻辑,更可能是由于数据传输的形式不符合服务器的预期。今天我们将探讨如何通过加密的方式来解决SwiftUI中的API请求问题。 背景 当你收到一…

作者头像 李华
网站建设 2026/5/1 19:45:31

手把手教你为STM32/GD32项目添加“出厂时间”与“运行时长”统计功能

实战指南&#xff1a;在STM32/GD32项目中实现设备生命周期追踪功能 引言 在产品级嵌入式系统开发中&#xff0c;设备生命周期管理是一个经常被忽视却至关重要的功能模块。想象一下&#xff0c;当你需要排查现场设备故障时&#xff0c;如果能准确知道这台设备的生产日期、首次启…

作者头像 李华