力扣【排序】:LCR 170. 交易逆序对的总数
- 题目描述:
在股票交易中,如果前一天的股价高于后一天的股价,则可以认为存在一个「交易逆序对」。请设计一个程序,输入一段时间内的股票交易记录 record,返回其中存在的「交易逆序对」总数。
- 算法思路:
- 考虑逆序数的定义是在一个数组中有两个下标分别为i,j的元素,有i < j,nums[i] > nums[j]。对于一个数组nums,可以考虑分治思路,先计算左侧子数组的逆序数,再计算右侧子数组的逆序数,最后计算左侧对于右侧的逆序数即可。
- 又想到,如果子数组分别有序,那么计算逆序数时如果nums[i] > nums[j],则从下标i开始到第一个子数组的最后一个元素内所有元素都可以和nums[j]构成逆序数,于是可以尝试在排序过程中计算逆序数,很显然这里使用归并排序是最合理的。
- 到这里其实思路已经很清晰了,我们只需要将计算逆序数的过程嵌入到归并排序中即可,并且由于归并排序实际的排序过程是依靠merge实现的,所以只需要在merge时计算子数组的逆序数即可,按照分析只需要在nums[i] > nums[j]并且i < j时将mid - i + 1累加到一个逆序数统计变量即可。
- 考虑当其中一个数组已经遍历结束的情况:如果左侧子数组遍历结束,那么右侧子数组剩余部分不会组成逆序数,因此不用计数,如果右侧子数组遍历结束,因为在遍历时必然出现过某个nums[i] > nums[j],所有的逆序数也已经被计算,所以剩余部分同样只需要正常排序即可。
- 并且题目要求数组最大长度为50000,显然逆序数最大不会超过int上限,用等差数组求和可知。
- 代码:
时间复杂度:归并递归层次为 log n向上取整,每层递归都会遍历数组,O(n log n),n为数组长度
空间复杂度:递归深度为O(log n),辅助数组O(n),结果为O(n)
class Solution {
private:
vector<int> tmp;
public:
int reversePairs(vector<int>& record) {
int size = record.size();
if (size == 0) {
return 0;
}
tmp.resize(size, 0);
return MergeHelper(record, 0, size - 1);
}
int MergeHelper(vector<int> &record, int left, int right) {
if (left == right) {
return 0;
}
int mid = left + (right - left) / 2;
int reverse_pairs = 0;
reverse_pairs += MergeHelper(record, left, mid);
reverse_pairs += MergeHelper(record, mid + 1, right);
reverse_pairs += Merge(record, left, mid, right);
return reverse_pairs;
}
int Merge(vector<int> &record, int left, int mid, int right) {
int reverse_count = 0;
for (int idx = left; idx <= right; ++idx) {
tmp[idx] = record[idx];
}
int i = left;
int j = mid + 1;
for (int k = left; k <= right; ++k) {
if (i == mid + 1) {
record[k] = tmp[j];
++j;
} else if (j == right + 1) {
record[k] = tmp[i];
++i;
} else if (tmp[i] > tmp[j]) {
record[k] = tmp[j];
reverse_count += mid - i + 1;
++j;
} else if (tmp[i] <= tmp[j]) {
record[k] = tmp[i];
++i;
}
}
return reverse_count;
}
};
浙公网安备 33010602011771号