题目
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
思考
- 这题很容易想到用暴力解法,但是由于数组过长,所以此时间复杂度是不能接受的
- 第二种想法是从后往前进行统计,将统计过的数字放入大顶堆,当前数字比大顶堆堆顶元素大时,则有堆大小即为逆序对数量,不断遍历。但是有可能当前数字比堆顶元素小,此时就要依次弹出堆顶元素进行比较,这样会提高复杂度
- 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;}
};