[LeetCode][LCR170]交易逆序对的总数

题目

LCR 170. 交易逆序对的总数

在股票交易中,如果前一天的股价高于后一天的股价,则可以认为存在一个「交易逆序对」。请设计一个程序,输入一段时间内的股票交易记录 record,返回其中存在的「交易逆序对」总数。

  • 示例 1:

输入:record = [9, 7, 5, 4, 6]
输出:8
解释:交易中的逆序对为 (9, 7), (9, 5), (9, 4), (9, 6), (7, 5), (7, 4), (7, 6), (5, 4)。

  • 限制:
    0 <= records.length <= 50000

思考

  1. 这题很容易想到用暴力解法,但是由于数组过长,所以此时间复杂度是不能接受的
  2. 第二种想法是从后往前进行统计,将统计过的数字放入大顶堆,当前数字比大顶堆堆顶元素大时,则有堆大小即为逆序对数量,不断遍历。但是有可能当前数字比堆顶元素小,此时就要依次弹出堆顶元素进行比较,这样会提高复杂度
  3. K 神利用了归并排序的思想:在合并的时候,实际上是两个递增序列的合并,所以当左边序列的一个元素大于右边序列的一个元素时,左边序列这个元素及其后面的所有元素都大于右边的这个元素,在合并的过程中顺便进行了逆序对的统计,时间复杂度为 O(NlogN)

解法1:归并——整体的临时数组

作者:Krahets
链接:https://leetcode.cn/leetbook/read/illustration-of-algorithm/o53yjd/

在这里插入图片描述

class Solution {
public:
    int reversePairs(vector<int>& record) {
        vector<int> tmp(record.size());
        return mergeSort(0, record.size() - 1, record, tmp);
    }
private:
    int mergeSort(int l, int r, vector<int>& record, vector<int>& tmp) {
        // 终止条件
        if (l >= r) return 0;
        // 递归划分
        int m = (l + r) / 2;
        int res = mergeSort(l, m, record, tmp) + mergeSort(m + 1, r, record, tmp);
        // 合并阶段
        int i = l, j = m + 1;
        for (int k = l; k <= r; k++)
            tmp[k] = record[k];
        for (int k = l; k <= r; k++) {
            if (i == m + 1)
                record[k] = tmp[j++];
            else if (j == r + 1 || tmp[i] <= tmp[j])
                record[k] = tmp[i++];
            else {
                record[k] = tmp[j++];
                res += m - i + 1; // 统计逆序对
            }
        }
        return res;
    }
};

解法2:归并——局部变量临时数组

此处利用了一般归并排序的写法,使用一局部变量临时数组,相对于解法1 较慢。
归并排序

class Solution {
public:
    int reversePairs(vector<int>& record) {
        return mergeSort(record, 0, record.size()-1);
    }
    int mergeSort(vector<int>& nums, int low, int high) {
        if(low>=high) return 0;
        int mid=(high+low)/2;
        //分
        int res=mergeSort(nums, low, mid) + mergeSort(nums, mid+1, high);
        //治
        vector<int> temp;
        temp.assign(nums.begin()+low, nums.begin()+high+1);
        int i=0, j=mid-low+1;
        for(int x=low; x<=high; ++x){
            if(i==mid-low+1) nums[x]=temp[j++];
            else if(j==high-low+1 || temp[j]>=temp[i]) nums[x]=temp[i++];
            else{
                nums[x]=temp[j++];
                //当左边的有序序列遍历到的数大于右边有序序列遍历到的数,则左边序列遍历到的数之后都大于右边序列遍历到的这个数
                res += mid-(i+low)+1;            
            } 
        }
        return res;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

__Witheart__

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值