题目
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;
}
};