优选算法:分治-归并排序

交易逆序对的总数

题目

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

逻辑

方法一:双指针逐个比较,会超时

方法二:采用归并排序的思想

  • 把数组分成两部分
  • 左半部分逆序对的个数为 a,右半部分逆序对的个数为 b,再从左区间/右区间各挑出一个元素,组成 c 个逆序对,此时逆序对的个数为 a+b+c
  • 从左区间/右区间各挑出一个元素的时候,保证左右区间有序,如果 numscur1 > numscur2,且数组是降序,那么 cur2,right 区间中的元素都比 numscur1 小,此时逆序对的个数 ret+=right-cur2+1,然后 cur1++

策略1:找出该数之后,有多少个比我小(盯着 cur1 看)

  • 如果都是从左侧开始比较
  • 找出该数之后,所有比 cur1 小的数之后,cur1才能 ++
  • 那么这时候我们需要使用升序数组还是降序数组呢?

假设数组有序排序后是升序

  • 如果 numscur1 <= numscur2,说明没有比 cur1 小的了,cur1++
  • 如果 numscur1 > numscur2,ret+=cur2-mid+1+1,cur2++,会出现重复计算的问题

假设数组有序排序后是降序

  • 如果 numscur1<=numscur2,cur2++
  • 如果 numscur1>numscur2,说明 cur2,right 都比 numscur1 小,ret+=right-cur2+1;此时比 numscur1 小的数都找完了,cur1++

总结:

找出该数之后,有多少个比我小,其实就是找右区间中所有比我小的元素中,最大的那个,又因为是从左到右找,所以用降序

策略2:找出该数之前,有多少个比我大(盯着 cur2 看)

  • 如果都是从左侧开始比较
  • 找出所有比 cur2 大的数之后,cur2 才能++
  • 那么这时候我们需要使用升序数组还是降序数组呢?

假设数组有序排序后是升序

  • 如果 numscur1 <= numscur2 ,cur1++
  • 如果 numscur1 > numscur2 ,说明 cur1,mid 都大于 numscur2,逆序对 ret+=mid-left+1,此时比 cur2 大的数都找到了,cur2++

假设数组有序排序后是降序

  • 如果 numscur1 > numscur2,逆序对 ret+=cur1-left+1,cur1++,会有重复计算的问题
  • 如果 numscur1<=numscur2,此时比 cur2 大的数都找完了,cur2++

总结:

找出该数之前,有多少个比我大,其实就是找左区间中所有比我大的元素中,最小的那个,又因为是从左到右,所以用升序

代码

cpp 复制代码
class Solution {
    vector<int> tmp;
public:
    int reversePairs(vector<int>& nums) {
        tmp.reserve(nums.size());
        return mergeSort(nums,0,nums.size()-1);
    }

    int mergeSort(vector<int>& nums,int left,int right)
    {
        if(left>=right) return 0;

        int mid=(left+right)>>1;

        // 逆序对的个数=
        // 左区间逆序对的个数a+
        // 右区间逆序对的个数c+
        // 左右区间各自挑出一个元素组成的逆序对个数c
        int ret=0;
        ret+=mergeSort(nums,left,mid);
        ret+=mergeSort(nums,mid+1,right);

        // 一边排序,一遍找逆序对
        int cur1=left,cur2=mid+1,i=left;
        while(cur1<=mid && cur2<=right)
        {
            // 找出该数之后,有多少个元素比我小,降序
            if(nums[cur1]<=nums[cur2]) tmp[i++]=nums[cur2++];
            else
            {
                ret+=right-cur2+1;
                tmp[i++]=nums[cur1++];
            }
        }
        while(cur1<=mid) tmp[i++]=nums[cur1++];
        while(cur2<=right) tmp[i++]=nums[cur2++];

        // 归并
        for(int j=left;j<=right;j++) nums[j]=tmp[j];

        return ret;
    }
};
相关推荐
非优秀程序员11 小时前
第05章 播放器大战,暴风、快播与那一夜|《互联网产品恩仇录》
算法
武子康17 小时前
驾驶轨迹换个坐标,就能给移动机器人用吗?
人工智能·算法·机器学习
lisw051 天前
Dayhoff的PAM替换矩阵理论评介!
线性代数·算法·矩阵
一起学安全技术1 天前
拆大模型 03|注意力的一句话版本:不用论文讲清 Attention
深度学习·算法·语言模型·transformer
Delightrs1 天前
Rust LeetCode238 算法题解
开发语言·算法·rust
自信150413057591 天前
重生之从0开始学习c++之——vector-中篇
c++·学习·算法
GoAI2 天前
DeepSeek Harness Memory (一)主流记忆插件总结
后端·算法·面试