跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

归并排序与数组中的逆序对算法解析

归并排序采用分治策略,将数组递归划分为左右子区间直至长度为 1,再合并有序子数组完成整体排序,时间复杂度为 O(nlogn)。数组逆序对问题可借助归并排序的合并阶段统计,当发现右侧元素小于左侧当前元素时,左侧剩余元素均与该右侧元素构成逆序对。文章提供 C++ 实现代码,详细解析归并排序流程及逆序对计算逻辑,帮助理解分治算法在排序与统计场景下的应用。

暖阳发布于 2026/3/23更新于 2026/10/25105 浏览
归并排序与数组中的逆序对算法解析

47.归并排序

题目链接:

215. 数组912. 排序数组 - 力扣(LeetCode)

题目描述:

文章配图

题目示例:

文章配图

解法 (归并排序):
算法思路:

归并排序的流程充分体现了「分而治之」的思想,大体过程分为两步:

分:将数组一分为二为两部分,一直分解到数组的长度为 1,使整个数组的排序过程被分为「左半部分排序」+「右半部分排序」;

治:将两个较短的「有序数组合并成一个长的有序数组」,一直合并到最初的长度。

C++ 算法代码:
class Solution {
public:
    //归并排序的算法
    vector<int> tmp; //用于存放两个有序数组合并后的结果
    
    void mergesort(vector<int>& nums, int left, int right) {
        if(left == right) {
            return;
        }
        //1、选择中间点划分区间
        int mid = (right - left) / 2 + left;
        //将数组分成两块:[left, mid] [mid + 1, right]
        //2、把左右区间排序
        mergesort(nums, left, mid);
        mergesort(nums, mid + 1, right);
        //3、将两个数组合并成一个有序数组
        int cur1 = left, cur2 = mid + 1, i = 0;
        while(cur1 <= mid && cur2 <= right) {
            tmp[i++] = nums[cur1] <= nums[cur2] ? nums[cur1++] : nums[cur2++];
        }
        //还有一边数组没有合并完
        while(cur1 <= mid) {
            tmp[i++] = nums[cur1++];
        }
        while(cur2 <= right) {
            tmp[i++] = nums[cur2++];
        }
        //只会进其中一个循环
        //将两个数组有序合并到tmp中后,再还原给原数组nums对应部分位置
        for(int i = left; i <= right; i++) {
            nums[i] = tmp[i - left]; //tmp数组每次都是以开头下标 0 的位置合并两个数组
        }
    }
    
    vector<int> sortArray(vector<int>& nums) {
        //归并实现:
        tmp.resize(nums.size());
        mergesort(nums, 0, nums.size() - 1);
        return nums;
    }
};
算法总结及流程解析:

文章配图

48.数组中的逆序对

题目链接:

LCR 170. 交易逆序对的总数 - 力扣(LeetCode)

题目描述:

文章配图

题目示例:

文章配图

解法(利用归并排序的过程——分治):
算法思路:

用归并排序求逆序数是很经典的方法,主要就是在归并排序的合并过程中统计出逆序对的数量,也就是在合并两个有序序列的过程中,能够快速求出逆序对的数量。

我们将这个问题分解成几个小问题,逐一破解这道题。 (注意:默认都是升序,如果掌握升序的话,降序的归并过程也是可以解决问题的。)

先解决第一个问题,为什么可以利用归并排序?

如果我们将数组从中间划分成两个部分,那么我们可以将逆序对产生的方式划分成三组:

逆序对中两个元素:全部从左数组中选择

逆序对中两个元素:全部从右数组中选择

逆序对中两个元素:一个选左数组另一个选右数组

根据排列组合的分类相加原理,三种情况下产生的逆序对的总和,正好等于总的逆序对数量。

而这个思路正好匹配归并排序的过程:

先排序左数组;

再排序右数组;

左数组和右数组合二为一。

因此,我们可以利用归并排序的过程,先求出左半数组中逆序对的数量,再求出右半数组中逆序对的数量,最后求出一个选择左边,另一个选择右边情况下逆序对的数量,三者相加即可。

解决第二个问题,为什么要这么做?

在归并排序合并的过程中,我们得到的是两个有序的数组。我们是利用数组的有序性,快速统计出逆序对的数量,而不是将所有情况都枚举出来。 最核心的问题,如何在合并两个有序数组的过程中,统计出逆序对的数量?合并两个有序序列时求逆序对的方法有两种:

  1. 快速统计出某个数前面有多少个数比它大;
  2. 快速统计出某个数后面有多少个数比它小;
C++ 算法代码:
class Solution {
public:
    int count = 0;
    vector<int> tmp;//用于存放两个有序数组合并后的结果
    
    int reversePairs(vector<int>& record) {
        tmp.resize(record.size());
        mergesort(record, 0, record.size() - 1);
        return count;
    }
    
    void mergesort(vector<int>& nums, int left, int right) {
        if(left >= right)//正常归并排序递归的结束条件不用包含 left>right
        //因为归并是分两块,最小的情况就是两个数分成各一个,不存在越界的情况
        //但是此题有空数组的案例,所以一开始 left=0,right=-1,要考虑在内
        {
            return;
        }
        //1、选择中间点划分区间
        int mid = (right - left) / 2 + left;
        //将数组分成两块:[left, mid] [mid + 1, right]
        //2、把左右区间排序
        mergesort(nums, left, mid);
        mergesort(nums, mid + 1, right);
        //3、判断两个有序数组一左一右的逆序对个数,并且将两个数组合并成一个有序数组
        int cur1 = left, cur2 = mid + 1, i = 0;
        //(1)将数组排成升序的思路:
        while(cur1 <= mid && cur2 <= right) {
            if(nums[cur1] <= nums[cur2]) {
                tmp[i++] = nums[cur1++];
            } else {
                //当第一次遇见 nums[cur1] > nums[cur2],说明 [cur1, mid] 区间所有值都大于 nums[cur2]
                //计算当前 nums[cur2] 的逆序对个数
                count += mid - cur1 + 1;
                tmp[i++] = nums[cur2++];
            }
        }
        //(2)将数组排成降序的思路:
        // while(cur1 <= mid && cur2 <= right)
        // {
        //     if(nums[cur1] > nums[cur2])
        //     {
        //         count += right - cur2 + 1;
        //         tmp[i++] = nums[cur1++];
        //     }
        //     else
        //     {
        //         tmp[i++] = nums[cur2++];
        //     }
        // }
        //处理还没有排序的剩余部分
        while(cur1 <= mid) {
            tmp[i++] = nums[cur1++];
        }
        while(cur2 <= right) {
            tmp[i++] = nums[cur2++];
        }
        //将两个数组有序合并到 tmp 中后,再还原给原数组 nums 对应部分位置
        for(int i = left; i <= right; i++) {
            nums[i] = tmp[i - left]; //tmp 数组每次都是以开头下标 0 的位置合并两个数组
        }
    }
};
算法总结及流程解析:

文章配图

文章配图

文章配图

目录

  1. 47.归并排序
  2. 题目链接:
  3. 题目描述:
  4. 题目示例:
  5. 解法 (归并排序):
  6. 算法思路:
  7. C++ 算法代码:
  8. 算法总结及流程解析:
  9. 48.数组中的逆序对
  10. 题目链接:
  11. 题目描述:
  12. 题目示例:
  13. 解法(利用归并排序的过程——分治):
  14. 算法思路:
  15. C++ 算法代码:
  16. 算法总结及流程解析:

更多推荐文章

查看全部
  • Linux 网络队列算法详解:FQ、Codel、PIE、CAKE 对比解析
  • 22 个优质 Python 学习网站推荐
  • Flutter 在 OpenHarmony 中应对跨域挑战:flutter_cors 实战方案
  • 2025 年 AIGC 六大核心趋势与落地应用分析
  • Flutter for OpenHarmony 集成 dart_openai 接入 AI 大模型
  • 2026 主流 AI 编程助手深度评测:文心快码、Copilot 与 Cursor
  • Python 开发 MongoDB 数据库 MCP Server 实战指南
  • FLUX.1-DEV BNB-NF4 部署指南:4bit 量化实现低显存 AI 绘画
  • MiniMax 海螺 AI:图片与文本生成高质量视频实战
  • ASP.NET Core 主机模型详解:Host、WebHost 与 WebApplication 对比与实践
  • C++ 测试框架 Catch2 快速上手指南
  • 扩散模型(Diffusion Model)原理与图像生成实战
  • Java 剪辑接单报价比价系统技术架构与源码解析
  • 基于大模型的语音转文字应用实现指南
  • Linux 匿名管道通信原理与代码实现
  • Java Swing 实现的个人所得税计算模拟器源码分享
  • AI 网络技术编程测试:从理论到实践
  • 在昇腾 NPU 上部署 Llama 大模型:全流程实战与踩坑指南
  • 基于 Python 与 Stable Diffusion 的马年新春营销素材生成实战
  • n8n 集成飞书机器人实战指南:从 Webhook 到 WebSocket 的迁移

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online