剑指offer 学习笔记 数组中的逆序对
面试题51:数组中的逆序对。在数组中的任意两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。如数组{7,5,6,4}中,一共存在5个逆序对。
直观解法是顺序扫描整个数组,每扫描到一个数字,逐个比较该数字和它后面的数字的大小,如果后面的数字比它小,则这两个数字组成一个逆序对。这种算法的时间复杂度是O(n²),太慢了。
当我们每扫描到一个数字时,不能拿它和后面每一个数字进行比较,否则时间复杂度就是O(n²),因此,我们可以考虑先比较两个相邻数字。
我们可以先将数组以中间位置元素为界分为两组,然后对每一组再以中间位置为界来分组,直到分成的两组中的其中一组只有一个元素,然后开始合并,下面是一个例子:
如上所示,每一次合并两个子数组时,这两个子数组都保证是有序的,合并的具体过程如下:
如上,我们通过两个指针计算逆序对数,如步骤(a)中,7大于6,因此对于7来说,逆序数为后部子数组中的指针指向的元素及子数组中该指针前面的元素数量之和,即为2,剩下的同理。上面的排序过程本质上就是归并排序,只不过多了一步统计逆序数的过程:
#include <iostream>
#include <vector>
using namespace std;
int InversePairs(vector<int>& nums, vector<int>& copy, int start, int end) { // 打工函数
if (start == end) {
return 0;
}
int middle = (start + end) >> 1; // 中间元素下标
int endLeft = middle; // 左部分数组的结尾
int startRight = middle + 1; // 右部分数组的开头
int left = InversePairs(copy, nums, start, endLeft); // 左部分数组中的逆序数,此处的copy应作为nums的实参,因为每次递归修改的是nums和copy中的一个数组
// 而下次的使用时需要用到修改过的内容,因此每次交换copy和nums的身份,就可以获取到上次改变的内容
// 也可以认为因为下面要判断时需要nums的两个子数组已经有序,因此要修改nums值,即把nums当成copy来修改
int right = InversePairs(copy, nums, startRight, end); // 右部分数组中的逆序数
int posInLeft = endLeft; // 指向左部分数组尾元素的指针
int posInRight = end; // 指向右部分数组尾元素的指针
int posInCopy = end; // 指向copy数组中存放排序结果的尾位置
int count = 0; // 计算当前两个数组合并过程中出现的逆序数的次数
while (posInLeft >= start && posInRight >= startRight) {
if (nums[posInLeft] > nums[posInRight]) {
count += posInRight - startRight + 1;
copy[posInCopy--] = nums[posInLeft--];
} else {
copy[posInCopy--] = nums[posInRight--];
}
}
// 以下两个for循环只能进入一个
for (; posInLeft >= start; --posInLeft) {
copy[posInCopy--] = nums[posInLeft];
}
for (; posInRight >= startRight; --posInRight) {
copy[posInCopy--] = nums[posInRight];
}
return count + left + right;
}
int InversePairs(vector<int>& nums) { // 函数入口
if (nums.size() == 0) {
return 0;
}
vector<int> copy(nums); // copy初始化为nums
int count = InversePairs(nums, copy, 0, nums.size() - 1);
return count;
}
int main() {
vector<int> nums = { 7,5,6,4 };
cout << InversePairs(nums) << endl;
}
由于归并排序的时间复杂度是O(nlogn),比直观解法的O(n²)要快,但这是用空间换时间,需要O(n)的空间消耗。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)