数组中的逆序对(C++)

目录

1 问题描述

1.1 输入描述:

1.2 示例1

1.3 示例2

2 解题思路

2.1 暴力解法

2.2 归并排序法

3 代码实现

3.1 暴力解法

3.2 归并排序法

4 代码解析

4.1 暴力解法

4.1.1 初始化

4.1.2 判断是否是逆序对

4.2 归并排序法

4.2.1 InversePairs 主函数

4.2.2 归并排序 merge_sort__ 递归拆分

4.2.3 归并 merge__ 统计逆序对

5 总结


1 问题描述

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P mod 1000000007
数据范围:  对于 50%50% 的数据, size≤104size≤104
对于 100%100% 的数据, size≤105size≤105

数组中所有数字的值满足 0≤val≤1090≤val≤109

要求:空间复杂度 O(n)O(n),时间复杂度 O(nlogn)O(nlogn)

1.1 输入描述:

题目保证输入的数组中没有的相同的数字

1.2 示例1

输入:

[1,2,3,4,5,6,7,0]

返回值:

7

1.3 示例2

输入:

[1,2,3]

返回值:

0

2 解题思路

2.1 暴力解法

通过两层嵌套循环,外层循环 i 遍历数组中的每个元素,内层循环 ji+1 开始,检查 nums[i] > nums[j] 是否成立,若成立则递增逆序对计数 p++。最终,返回 p % 1000000007 以避免结果溢出。

2.2 归并排序法

采用归并排序的方法高效计算数组中的逆序对。在 InversePairs 函数中,调用 merge_sort__ 递归地对数组进行归并排序,并在合并过程中计算逆序对。merge_sort__ 先递归地将数组拆分为左右两部分,再在 merge__ 过程中合并,同时统计逆序对。合并时,如果左半部分的 arr[i] > arr[j]j 来自右半部分),则意味着 arr[i] 及其后续所有元素均大于 arr[j],因此逆序对数 ret 需要累加 (mid - i + 1),并取模 1000000007 以防溢出。

3 代码实现

3.1 暴力解法

    int InversePairs(vector<int>& nums) {
        // write code here
        long long p = 0;
        int n = nums.size();
        for(int i = 0; i+1 < n; i++)
        {
            for(int j = i+1; j < n; j++)
            {
                if(nums[i] > nums[j])
                {
                    p++;
                }
            }
        }
        return fmod(p, 1000000007);
    }

3.2 归并排序法

class Solution {
private:
    const int kmod = 1000000007;
public:
    int InversePairs(vector<int> data) {
        int ret = 0;
        merge_sort__(data, 0, data.size() - 1, ret);
        return ret;
    }


    void merge_sort__(vector<int> &arr, int l, int r, int &ret) {
        if (l >= r) {
            return;
        }

        int mid = l + ((r - l) >> 1);
        merge_sort__(arr, l, mid, ret);
        merge_sort__(arr, mid + 1, r, ret);
        merge__(arr, l, mid, r, ret);
    }

    void merge__(vector<int> &arr, int l, int mid, int r, int &ret) {
        vector<int> tmp(r - l + 1);
        int i = l, j = mid + 1, k = 0;

        while (i <= mid && j <= r) {
            if (arr[i] > arr[j]) {
                tmp[k++] = arr[j++];
                // 奥妙之处
                ret += (mid - i + 1);
                ret %= kmod;
            }
            else {
                tmp[k++] = arr[i++];
            }
        }

        while (i <= mid) {
            tmp[k++] = arr[i++];
        }
        while (j <= r) {
            tmp[k++] = arr[j++];
        }

        for (k = 0, i = l; i <= r; ++i, ++k) {
            arr[i] = tmp[k];
        }
    }
};

4 代码解析

4.1 暴力解法

4.1.1 初始化
        long long p = 0;
        int n = nums.size();

初始化,p用来计算逆序对个数,n为循环边界。 

4.1.2 判断是否是逆序对
        for(int i = 0; i+1 < n; i++)
        {
            for(int j = i+1; j < n; j++)
            {
                if(nums[i] > nums[j])
                {
                    p++;
                }
            }
        }

使用两层嵌套循环统计逆序对,外层循环 i遍历数组的前 n-1 个元素。内层循环 ji+1 开始,遍历 i 之后的元素,检查 nums[i] > nums[j] 是否成立。如果满足 nums[i] > nums[j],则 p++ 记录一个逆序对。

4.2 归并排序法

4.2.1 InversePairs 主函数
int InversePairs(vector<int> data) {
    int ret = 0;
    merge_sort__(data, 0, data.size() - 1, ret);
    return ret;
}

ret 变量用于存储逆序对的个数。调用 merge_sort__data 进行归并排序,同时统计逆序对。

4.2.2 归并排序 merge_sort__ 递归拆分
void merge_sort__(vector<int> &arr, int l, int r, int &ret) {
    if (l >= r) {
        return;
    }

    int mid = l + ((r - l) >> 1);
    merge_sort__(arr, l, mid, ret);
    merge_sort__(arr, mid + 1, r, ret);
    merge__(arr, l, mid, r, ret);
}

该函数不断递归拆分数组,直到 l >= r(即子数组长度为 1)。递归调用 merge_sort__ 对左右子数组分别排序。最后调用 merge__ 进行合并并计算逆序对。 

4.2.3 归并 merge__ 统计逆序对
void merge__(vector<int> &arr, int l, int mid, int r, int &ret) {
    vector<int> tmp(r - l + 1);
    int i = l, j = mid + 1, k = 0;

    while (i <= mid && j <= r) {
        if (arr[i] > arr[j]) {
            tmp[k++] = arr[j++];
            // 统计逆序对数量
            ret += (mid - i + 1);
            ret %= kmod;
        } else {
            tmp[k++] = arr[i++];
        }
    }

    while (i <= mid) tmp[k++] = arr[i++];
    while (j <= r) tmp[k++] = arr[j++];

    for (k = 0, i = l; i <= r; ++i, ++k) {
        arr[i] = tmp[k];
    }
}

该方法利用双指针合并两个有序子数组,并在合并过程中统计逆序对。指针 i 遍历左半部分(lmid),j 遍历右半部分(mid + 1r)。当 arr[i] > arr[j] 时,说明 arr[i] 及其后续所有元素(mid - i + 1 个)都大于 arr[j],构成逆序对,累加 ret 并对 1000000007 取模防止溢出。排序时,较小的元素先存入 tmp,保持整体有序,最终将 tmp 复制回 arr,完成归并排序。

5 总结

本文介绍了两种计算数组逆序对的方法:暴力解法和归并排序法。暴力解法使用两层嵌套循环,逐一比较元素,时间复杂度为O(n^2),适用于小规模数据。归并排序法利用归并排序的分治思想,结合双指针合并两个有序子数组,并在合并过程中统计逆序对,时间复杂度降为O(n log n),适用于大规模数据。具体实现中,递归地拆分数组,合并时通过比较左右子数组元素,累加逆序对数量。归并排序法高效、稳定,且满足题目对时间和空间的复杂度要求,是解决该问题的推荐方案。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/981082.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

iphone上ios设备开启safari开发者debug模式,配合mac电脑使用

1.mac操作 mac的safari上打开开发者模式&#xff0c;打开显示网页开发者功能 2.开启IPhone的Safari调试模式 启用 Web 检查 功能&#xff0c;打开 iPhone 依次进入 设置 > Safari浏览器 > 高级 > 网页检查器 > 启用。 3.调试步骤 先用IPhone 的Safari打开要调试…

Flutter 学习之旅 之 flutter 在 Android 端进行简单的打开前后相机预览 / 拍照保存

Flutter 学习之旅 之 flutter 在 Android 端进行简单的打开前后相机预览 / 拍照保存 目录 Flutter 学习之旅 之 flutter 在 Android 端进行简单的打开前后相机预览 / 拍照保存 一、简单介绍 二、简单介绍 camera 三、安装 camera 四、简单案例实现 五、关键代码 一、简单…

[BUUCTF]web--wp(持续更新中)

ps:文章所引用知识点链接&#xff0c;如有侵权&#xff0c;请联系删除 [极客大挑战 2019]EasySQL 题目类型&#xff1a;简单SQL注入 发现是登录页面&#xff0c;用万能登录方法测试&#xff0c;两种语句均能解出flag [极客大挑战 2019]Havefun 题目类型&#xff1a;代码审计…

【Java项目】基于SpringBoot的CSGO赛事管理系统

【Java项目】基于SpringBoot的CSGO赛事管理系统 技术简介&#xff1a;采用SpringBoot框架、Java语言、MySQL数据库等技术实现。 系统简介&#xff1a;CSGO赛事管理系统是一个基于B/S架构的管理系统&#xff0c;主要功能包括前台和后台管理模块。前台系统功能模块分为&#xf…

Grafana接入Zabbix数据源

1. 对接 Zabbix 1.1 安装 Zabbix 插件 在线安装&#xff1a; 1.2 配置 Zabbix 数据源 点击 Configuration > Data Sources > Add data source。选择 Zabbix&#xff0c;填写&#xff1a; URL&#xff1a;http://<zabbix-server>/api_jsonrpc.phpUsername&#x…

UnrealEngine UE5 可视化 从地球观察火星 金星 土星 运动轨迹

视频参考&#xff1a;https://www.bilibili.com/video/BV1KpXSYdEdo/ 从地球观察土星的运动轨迹 从地球观察火星 轨迹 从地球观察金星的运动轨迹

【鸿蒙操作系统】- 1:实习阶段的一些总结

本文目录 1. 序2.鸿蒙与欧拉的概念微内核LiteOS鸿蒙微内核POSIX标准 3.实习干了些什么身份鉴别访问控制恶意代码防范安全审计入侵防范性能压测检查系统版本网络测试常见的linux测试命令 1. 序 之前在某国企实习的时候&#xff0c;有幸参与了鸿蒙系统、鸿蒙欧拉的项目&#xff…

张岳教授:语言模型推理与泛化研究 | ICLR 2025 特邀报告与团队专场

点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入&#xff01; AITIME 01 ICLR 2025预讲会特邀报告 AITIME 02 ICLR 2025预讲会西湖大学张岳老师实验室专场 01 AI生成文本的自动化检测 Glimpse: Enabling White-Box Methods to Use Proprietary Models for Zero-Shot LLM-Ge…

Qt显示一个hello world

一、显示思路 思路一&#xff1a;通过图形化方式&#xff0c;界面上创建出一个控件显示。 思路二&#xff1a;通过编写C代码在界面上创建控件显示。 二、思路一实现 点开 Froms 的 widget.ui&#xff0c;拖拽 label 控件&#xff0c;显示 hello world 即可。 qmake 基于 .…

Qt基础入门-详解

前言 qt之路正式开启 &#x1f493; 个人主页&#xff1a;普通young man-CSDN博客 ⏩ 文章专栏&#xff1a;C_普通young man的博客-CSDN博客 ⏩ 本人giee: 普通小青年 (pu-tong-young-man) - Gitee.com 若有问题 评论区见&#x1f4dd; &#x1f389;欢迎大家点赞&#x1f44…

React Native从入门到进阶详解

React Native知识框架从入门到进阶的问题。首先需要结合我搜索到的资料来整理出结构化的内容。证据中有多本书籍和文章&#xff0c;可能会涉及不同的章节和重点&#xff0c;需要仔细梳理。 首先&#xff0c;根据邱鹏源的《React Native精解与实战》将知识分为入门和进阶两大部分…

win本地vscode通过代理远程链接linux服务器

时间&#xff1a;2025.2.28 1. win本地下载nmap.exe nmap官网 https://nmap.org/或者 https://nmap.org/download#windows下载win版本并安装。 2. vscode插件Remote-SSH 插件下载Remote-SSH 3. 配置 按照图中顺序配置ssh 1.点击左侧工具栏的“小电视”图标 2.点击ssh的…

MIT 6.S184 流匹配与扩散模型公开课

课程简介 MIT 2025年开设的关于流匹配算法与扩散模型的新课&#xff0c;6.S184: Generative AI with Stochastic Differential Equations&#xff08;生成式人工智能与随机微分方程&#xff09;&#xff0c;授课教师是Peter Holderrieth和Ezrah Erives。 生成式AI是一种能创建…

SQL server配置ODBC数据源(本地和服务器)

本地配置 1. 控制面板中找到系统ODBC数据源&#xff08;打开控制面板直接搜&#xff09; 2. 选择“系统DSN”&#xff0c;点击“添加” 3. 选择“SQL server” 4. 名称和描述自己填&#xff0c;服务器选择本机设备名称 5. 选择ID和密码验证&#xff0c;并填写本地SQL server登…

JVM线程分析详解

java线程状态&#xff1a; 初始(NEW)&#xff1a;新创建了一个线程对象&#xff0c;但还没有调用start()方法。运行(RUNNABLE)&#xff1a;Java线程中将就绪&#xff08;ready&#xff09;和运行中&#xff08;running&#xff09;两种状态笼统的称为“运行”。 线程对象创建…

Redis - 高可用实现方案解析:主从复制与哨兵监控

文章目录 Pre概述Redis 高可用实现方案一、主从复制机制1.1 全量同步流程1.2 增量同步&#xff08;PSYNC&#xff09;流程 二、哨兵监控机制2.1 故障转移时序流程 三、方案对比与选型建议四、生产环境实践建议 Pre Redis-入门到精通 Redis进阶系列 Redis进阶 - Redis主从工作…

栈和队列的模拟实现

文章目录 一. 回顾栈和队列二. stack的模拟实现stack.hstack.cpp 三. queue的模拟实现queue.htest.cpp 四. 了解dequeuevector和list都有各自的缺陷deque 总结 一. 回顾栈和队列 回顾一下栈和队列 栈&#xff1a;stack&#xff1a;后进先出 _ 队列&#xff1a;queue&#xf…

【Linux】之【Bug】VMware 虚拟机开机 一直卡在黑屏左上角下划线闪烁界面

解决 参考&#xff1a; 解决Ubuntu20.04 开机黑屏光标闪烁进不去系统 Centos根目录100%解决思路 当前界面 ctrlaltf3-f6 暂时进入终端界面 df -h 查看发现根目录 磁盘空间已满 执行命令 查看当前目录占用内存明细 sudo du -h -x --max-depth1清理无用的大内存文件 或者安装…

【uniapp】离线打包uniapp为apk详细步骤

先看效果 登录页面的图片由于来自于图鸟官网&#xff0c;这里没有显示。 离线打包uniapp为apk 运行环境&#xff1a;华为mate30&#xff0c;已经升级为鸿蒙系统。 参考文档 https://blog.csdn.net/xiaoyao_studio/article/details/144076431 https://juejin.cn/post/739…

【通俗讲解电子电路】——从零开始理解生活中的电路(一)

导言&#xff1a;电子电路为什么重要&#xff1f; ——看不见的“魔法”&#xff0c;如何驱动你的生活&#xff1f; 清晨&#xff0c;当你的手机闹钟响起时&#xff0c;你可能不会想到&#xff0c;是电子电路在精准控制着时间的跳动&#xff1b;当你用微波炉加热早餐时&#…