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 <= record.length <= 50000
以下是针对 LCR 170 题目的完整解决方案,我将提供多种解法,并详细解释每种方法的思路和实现:
方法一:归并排序(推荐)
class Solution {
private:
int mergeSort(vector<int>& record, int left, int right) {
// 递归终止条件
if (left >= right) return 0;
// 分治:划分区间
int mid = left + (right - left) / 2;
int count = mergeSort(record, left, mid) +
mergeSort(record, mid + 1, right);
// 合并过程:统计逆序对
vector<int> temp(right - left + 1);
int i = left, j = mid + 1, k = 0;
while (i <= mid && j <= right) {
// 如果左边元素大于右边元素,则形成逆序对
if (record[i] > record[j]) {
// 重点:当前右边元素与左边所有剩余元素都可以形成逆序对
count += mid - i + 1;
temp[k++] = record[j++];
} else {
temp[k++] = record[i++];
}
}
// 处理剩余元素
while (i <= mid) temp[k++] = record[i++];
while (j <= right) temp[k++] = record[j++];
// 将排序后的结果复制回原数组
for (int p = 0; p < k; p++) {
record[left + p] = temp[p];
}
return count;
}
public:
int reversePairs(vector<int>& record) {
// 边界处理
if (record.empty()) return 0;
// 调用归并排序进行逆序对统计
return mergeSort(record, 0, record.size() - 1);
}
};
方法二:树状数组(离散化)
class Solution {
private:
// 树状数组基本操作
vector<int> tree;
void update(int index, int val) {
while (index < tree.size()) {
tree[index] += val;
index += index & (-index);
}
}
int query(int index) {
int sum = 0;
while (index > 0) {
sum += tree[index];
index -= index & (-index);
}
return sum;
}
public:
int reversePairs(vector<int>& record) {
if (record.empty()) return 0;
// 1. 离散化处理
vector<int> sorted = record;
sort(sorted.begin(), sorted.end());
sorted.erase(unique(sorted.begin(), sorted.end()), sorted.end());
// 2. 初始化树状数组
tree.resize(sorted.size() + 1, 0);
// 3. 统计逆序对
int result = 0;
for (int i = record.size() - 1; i >= 0; i--) {
// 找到当前元素在排序后数组中的排名
int rank = lower_bound(sorted.begin(), sorted.end(), record[i]) - sorted.begin() + 1;
// 查询比当前元素小的元素个数
result += query(rank - 1);
// 更新树状数组
update(rank, 1);
}
return result;
}
};
方法三:暴力解法(仅供参考,时间复杂度O(n²))
class Solution {
public:
int reversePairs(vector<int>& record) {
int cnt = 0;
for (int i = 0; i < record.size(); i++) {
for (int j = i + 1; j < record.size(); j++) {
if (record[i] > record[j]) {
cnt++;
}
}
}
return cnt;
}
};
解题思路详解
1. 归并排序解法(推荐)
- 核心思想:在归并排序的合并过程中统计逆序对
- 时间复杂度:O(n log n)
- 空间复杂度:O(n)
- 优点:高效且易于理解
2. 树状数组解法
- 核心思想:通过离散化和树状数组统计逆序对
- 时间复杂度:O(n log n)
- 空间复杂度:O(n)
- 优点:可处理更大规模数据
3. 暴力解法
- 核心思想:直接遍历数组,逐个比较
- 时间复杂度:O(n²)
- 空间复杂度:O(1)
- 缺点:对于大规模数据会超时
建议
- 对于本题,归并排序解法最为推荐
- 面试中,建议先说明暴力解法,然后优化到归并排序或树状数组
- 掌握多种解法,灵活应对不同场景
复杂度分析
- 时间复杂度:O(n log n)
- 空间复杂度:O(n)
核心要点
- 理解逆序对的定义
- 熟悉归并排序思想
- 能够在归并过程中统计逆序对
- 注意边界条件处理
希望这个详细的解答能帮助你彻底理解这道题!