LCR 170. 交易逆序对的总数

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)

核心要点

  1. 理解逆序对的定义
  2. 熟悉归并排序思想
  3. 能够在归并过程中统计逆序对
  4. 注意边界条件处理

希望这个详细的解答能帮助你彻底理解这道题!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

g-zh

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

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

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

打赏作者

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

抵扣说明:

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

余额充值