归并排序——逆序数对的统计

逆序数对的统计

题目描述

运行代码

#include <iostream>
using namespace std;
#define LL long long 
const int N = 1e5 + 5;
int a[N], tmp[N];
LL merge_sort(int q[], int l, int r)
{
    if (l >= r)
 return 0;  
    int mid = l + r >> 1;  
    LL res = merge_sort(q, l, mid) + merge_sort(q, mid + 1, r);    
    int k = 0, i = l, j = mid + 1;
    while (i <= mid && j <= r)
        if (q[i] <= q[j]) 
   tmp[k ++ ] = q[i ++ ];
        else
        {
            res += mid - i + 1;
            tmp[k ++ ] = q[j ++ ];
        }
    while (i <= mid)
 tmp[k ++ ] = q[i ++ ];
    while (j <= r)
 tmp[k ++ ] = q[j ++ ];
    for (i = l, j = 0; i <= r; i ++, j ++ )
 q[i] = tmp[j];  
    return res;
}
int main()
{
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i ++ ) 
scanf("%d", &a[i]);    
    cout << merge_sort(a, 0, n - 1) << endl;  
    return 0;
}

代码思路

宏定义与常量

  • #define LL long long:定义LLlong long类型的别名,用于存储较大的整数,例如逆序对的数量。
  • const int N = 1e5 + 5;:定义数组的最大容量,适用于最多100,005个元素的数组。

函数merge_sort此函数递归地将数组分成更小的部分,然后合并这些部分,同时计算逆序对总数。

  • 参数q[]是要排序的数组,lr分别是当前子数组的左右边界(索引)。
  • 基础情况:当左边界大于等于右边界时(即子数组只有一个或没有元素),返回0,表示该子数组没有逆序对。
  • 分解:计算中间索引mid,递归地对左右两部分[l, mid][mid+1, r]进行排序并计算逆序对,将结果累加到res中。
  • 合并
    • 初始化辅助数组tmp和三个指针k, i, j。当i未超过midj未超过r时,比较q[i]q[j]的大小:
      • 如果q[i] <= q[j],说明当前元素不会形成新的逆序对,将q[i]放入tmp,并移动ik
      • 否则,所有q[i]q[mid]之间的元素都比q[j]大,因此增加了mid - i + 1个逆序对,将q[j]放入tmp,移动jk
    • 分别处理剩余的元素,将它们依次放入tmp。将tmp中的元素复制回原数组q
  • 返回值:最终返回整个数组排序过程中的逆序对总数res
  • 主函数main读取数组长度n。通过循环读取数组a中的每个元素。调用merge_sort函数,传入数组a和其边界(0和n-1),输出计算得到的逆序对总数。

改进思路

  • 使用指针直接操作数组:在mergeSortAndCount函数中,直接使用指针ijk而非索引,这在某些情况下可以略微提高访问效率。
  • 保持数组作为参数:继续使用原生数组作为函数参数,保持与原始代码结构的一致性。
  • 代码格式和可读性:调整代码格式,确保良好的可读性和一致性,比如增加必要的空格和换行。
  • 去除不必要的类型别名:保留int64_t作为长整型的别名,因为它清晰地表达了数据类型的目的。

改进代码

#include <iostream>
using namespace std;
typedef long long int64_t;
// 使用指针代替数组索引来优化访问速度
int64_t mergeSortAndCount(int a[], int temp[], int left, int right) {
    if (left >= right) return 0;   
    int mid = left + (right - left) / 2;   
    // 递归排序并计数
    int64_t inv_count = mergeSortAndCount(a, temp, left, mid);
    inv_count += mergeSortAndCount(a, temp, mid + 1, right);   
    // 合并阶段计算逆序对
    int i = left, j = mid + 1, k = left;
    while (i <= mid && j <= right) {
        if (a[i] <= a[j]) {
            temp[k++] = a[i++];
        } else {
            inv_count += mid - i + 1;
            temp[k++] = a[j++];
        }
    }  
    // 复制剩余元素
    while (i <= mid) temp[k++] = a[i++];
    while (j <= right) temp[k++] = a[j++];    
    // 将排序结果复制回原数组
    for (int p = left; p <= right; ++p) a[p] = temp[p];   
    return inv_count;
}
int main() {
    const int N = 1e5 + 10;
    int a[N], temp[N];
    int n;
    scanf("%d", &n);   
    for (int i = 0; i < n; ++i) scanf("%d", &a[i]);   
    int64_t inv_count = mergeSortAndCount(a, temp, 0, n - 1);
    cout << inv_count << endl;   
    return 0;
}

其他方法(使用向量) AI生成

#include <iostream>
#include <vector>

using namespace std;
using int64_t = long long;

// 合并排序并计数逆序对
int64_t mergeSortAndCount(vector<int>& array, vector<int>& temp, int left, int right) {
    if (left >= right) return 0;
    
    int mid = left + (right - left) / 2;
    
    // 递归排序左右两边,并计算逆序对
    int64_t inv_count = mergeSortAndCount(array, temp, left, mid);
    inv_count += mergeSortAndCount(array, temp, mid + 1, right);
    
    // 合并阶段计算逆序对
    int i = left, j = mid + 1, k = 0;
    while (i <= mid && j <= right) {
        if (array[i] <= array[j]) {
            temp[k++] = array[i++];
        } else {
            inv_count += mid - i + 1; // 计算逆序对
            temp[k++] = array[j++];
        }
    }
    // 处理剩余元素
    while (i <= mid) temp[k++] = array[i++];
    while (j <= right) temp[k++] = array[j++];
    
    // 将排序结果复制回原数组
    copy(temp.begin(), temp.begin() + k, array.begin() + left);
    
    return inv_count;
}

int main() {
    int n;
    cin >> n;
    vector<int> a(n);
    for (int& elem : a) cin >> elem;
    
    vector<int> temp(n); // 临时数组用于合并
    cout << mergeSortAndCount(a, temp, 0, n - 1) << endl;
    
    return 0;
}
  1. 添加函数注释:解释每个函数的作用,特别是merge_sort中的逻辑。
  2. 使用更明确的变量名:如将q[]改为array[],使代码意图更清晰。
  3. 去除全局变量:尽量减少全局变量的使用,改用函数参数传递。
  4. 优化类型别名的可读性:将LL改为更明确的别名,如typedef long long int64_t;
  5. 使用C++风格的输入输出:完全替换scanfprintfcincout

归并、插入和冒泡排序比较

归并排序

  • 优点:时间复杂度在平均和最坏情况下都为 ,性能比较稳定,适合大规模数据排序。
  • 缺点:需要额外的空间用于合并。

插入排序

  • 优点:对于接近有序的数据表现非常好,在小规模数据或部分有序数据上效率可能较高,代码简单。
  • 缺点:最坏情况时间复杂度为 。

冒泡排序

  • 优点:实现简单。
  • 缺点:时间复杂度较差,也是 。

一般来说,在数据规模较大且对性能要求较高时,归并排序通常表现更好。但如果数据规模较小或者数据有一定的特殊性(如接近有序),插入排序可能更合适。而冒泡排序相对来说在大多数情况下效率较低,较少被优先选用。

使用归并排序解决逆序数对统计问题思路:

在归并排序的合并过程中,当我们比较左右两部分元素时,左边部分一个较大元素如果出现在右边部分较小元素之前,那么就构成一个逆序数对。

当左边当前元素大于右边当前元素时,由于左右两边都是已排序的,那么左边该元素之后的所有元素与右边当前元素都构成逆序数对,数量为左边当前未处理元素的数量。我们可以在合并的同时统计这样的逆序数对数量。通过递归地执行归并排序,不断在合并过程中累加逆序数对的数量,最终就能得到总的逆序数对数量。

例如,假设有数组 [3, 1, 4, 2],在第一次合并 [3] 和 [1] 时,因为 3 大于 1,此时就找到一个逆序数对,然后继续递归合并其他部分并统计。

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

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

相关文章

QT-轻量级的笔记软件MyNote

MyNote v2.0 一个轻量级的笔记软件&#x1f4d4; Github项目地址: https://github.com/chandlerye/MyNote/tree/main 应用简介 MyNote v2.0 是一款个人笔记管理软件&#xff0c;没有复杂的功能&#xff0c;旨在提供便捷的笔记记录、管理以及云同步功能。基于Qt 6.6.3 个人开…

【Qt】Qt常见的数据类型

思维导图 学习目标 一、基础类型 因为Qt是一个C的框架&#xff0c;因此C的语法和数据类型在Qt中都是被支持的&#xff0c;但是Qt中也是定义了一些属于自己的数据类型&#xff0c;不过&#xff0c;好多数据类型都是对C的数据类型进行封装&#xff0c;下面来简要介绍一下这些基…

kafka集成SpringBoot api编写教程

1.新建项目 用的idea是20222.1.3版本&#xff0c;没有Spring Initializr 插件&#xff0c;不能直接创建springboot项目 可以在以下网址创建项目&#xff0c;下载后解压&#xff0c;然后用idea打开项目即可 1.1 在 https://start.spring.io/ 上创建项目 1.2上传到linux&#x…

第十四周 6.4 内部类部分知识点

一、理解 1.定义在一个类内部的类称为内部类 2.语法: class 类名{ class 类名{} } 3.内部类编译之后生成独立的.class文件&#xff0c;文件命名为:外部类类名$内部类的类名.class 4.内部类分类:成员内部类、静…

Layui弹框中设置输入框自动获取焦点无效/Layui设置Input框自动获取焦点无效,怎么办?

1、问题概述? 有时候为了用户体验,期望当弹框打开的时候,指定的输入框能自动的获取焦点,用户就可以直接输入了。提升了用户体验。但有时候设置的时候没有效果。 2、正常的设置自动获取焦点方式 【input框设置方式】 使用关键字autofocus <input type="text&quo…

OPenCV的重要结构体Mat

一 Mat Mat是什么&#xff1f; Mat有什么好处&#xff1f; class CV_EXPORTS Mat{ public: ... int dims;//维数 int rows,cols;//行列数 uchar *data;//存储数据的指针 int *refcount;//引用计数 ...};二 Mat属性 三 Mat拷贝 1 Mat浅拷贝 Mat A Aimread(file,IMREAD_COLOR) …

Java入门教程上

常见的cmd命令 类 class 字面量 数据类型 输入 public static void main(String[] args) {Scanner anew Scanner(System.in);int na.nextInt();int ma.nextInt();System.out.println(mn);} } 算数运算符 package wclg;public class test {public static void main(String[] ar…

网络安全难学吗?2024该怎么系统学习网络安全?

学习网络安全需要循序渐进&#xff0c;由浅入深。很多人对网络安全进行了解以后&#xff0c;就打算开始学习网络安全&#xff0c;但是又不知道怎么去系统的学习。 网络安全本身的知识不难&#xff0c;但需要学习的内容有很多&#xff0c;其中包括Linux、数据库、渗透测试、等保…

【算法专题--链表】环形链表--高频面试题(图文详解,小白一看就会!!)

目录 一、前言 二、什么是环形链表&#xff1f; &#x1f95d; 环形链表的概念详解 &#x1f347; 环形链表的特点 &#x1f34d;环形链表的延申问题 三、高频面试题 ✨环形链表 ✨环形链表II ✨环形链表的环长 四、总结 五、共勉 一、前言 环形链表&#xff0c;可以说是…

单臂路由的配置(思科、华为)

#交换设备 不同vlan属于不同广播域&#xff0c;不能互相通信&#xff0c;他们配置的是不同网段的IP地址&#xff0c;针对不同网段的IP地址进行通信&#xff0c;就需要用到路由技术 实现不同vlan之间的通信技术有两种 单臂路由三层交换 单臂路由 一、思科设备的单臂路由配…

Understanding Diffusion Objectives as the ELBO with Simple Data Augmentation

Understanding Diffusion Objectives as the ELBO with Simple Data Augmentation 引言 本文前作 VDM 已经推导出了扩散模型可以将优化 ELBO 作为目标函数。然而现在 FID &#xff08;也就是感知质量&#xff09;最好的模型还是用的其他目标函数&#xff08;如 DDPM 的噪声预…

矩阵LU分解的应用

矩阵LU分解在机器学习和深度学习中的应用广泛&#xff0c;主要用于解决以下问题&#xff1a; 线性方程组求解&#xff1a;LU分解可以有效地解决线性方程组&#xff0c;这在训练模型时非常有用。矩阵求逆&#xff1a;在一些机器学习算法中&#xff0c;需要进行矩阵求逆操作&…

二维鱼游CFD代码

最近学了会Julia&#xff0c;参考了原作者的shark&#xff0c;做一下基于airfoils 2D的鱼游&#xff0c;暂时没想好有什么需要深入研究的&#xff0c;代码公开如下&#xff1a; 鱼身是naca0016&#xff0c;然后一些参数可以参考我以前发的论文。 using WaterLily, StaticArra…

46.django - 多语言配置

1.Django 多语言基础知识 多语言站点可以让不同语言的用户更好地使用和理解网站内容&#xff0c;提升用户体验和覆盖范围。为了实现多语言功能&#xff0c;我们将使用Django内置的国际化和本地化支持。我收集了一些知识点整理在这一部分&#xff0c;感兴趣的可以看看。直接跳过…

k8s面试题大全,保姆级的攻略哦(三)

目录 1、简述ETCD及其特点? 2、简述ETCD适应的场景? 3、简述什么是Kubernetes? 4、简述Kubernetes和Docker的关系? 5、简述Kubernetes中什么是Minikube、Kubectl、Kubelet? 6、简述Kubernetes常见的部署方式? 7、简述Kubernetes如何实现集群管理? 8、简述Kubern…

双指针数组问题

删除有序数组中的重复项 重点在于p1 class Solution {public int removeDuplicates(int[] nums) {if(nums.length0) return 0;int p10,p21;while(p2<nums.length){if(nums[p2]!nums[p1]){nums[p1]nums[p2];}else p2;}return p11;} } class Solution {public void moveZeroe…

tkinter+火山引擎+python实现语音识别聊天机器人

想要做一款能通过语音识别来聊天的智能机器人,首先需要能通过麦克风录制语音进行识别转换成文字,将文字发送给机器人得到聊天结果,并能将返回的文字转换成语音进行合成,之后再通过本地播放语音实现语音交互。 架构: 实现步骤 一、本地录音 本地录音可以通过pyAudio库实…

【机器学习】Qwen2大模型原理、训练及推理部署实战

目录​​​​​​​ 一、引言 二、模型简介 2.1 Qwen2 模型概述 2.2 Qwen2 模型架构 三、训练与推理 3.1 Qwen2 模型训练 3.2 Qwen2 模型推理 四、总结 一、引言 刚刚写完【机器学习】Qwen1.5-14B-Chat大模型训练与推理实战 &#xff0c;阿里Qwen就推出了Qwen2&#x…

半年了,3588来了

端午两天&#xff0c;LAB1964又搞了新东西&#xff0c;RK3588&#xff0c;已经算是千呼万唤始出来&#xff0c;等的时间也是足够久了。 ——价格贵 RK3588 是真的贵&#xff0c;实验室老板贴了10片3588套片&#xff0c;就花了将近4000块钱&#xff0c;所以说&#xff0c;决定要…

kali2022安装教程(附安装包)

第一步&#xff1a;下载镜像文件 百度网盘下载https://pan.baidu.com/s/1efRQGFTbq6Kgw9axLOmWzg?pwdemxf 第二步&#xff1a;打开Vmware 第三步&#xff1a;进行各项配置 创建新的虚拟机&#xff0c;选择高级&#xff0c;然后下一步 直接默认下一步 选择稍后安装然后下…