493. 翻转对



1.题目

493. 翻转对

给定一个数组 nums ,如果 i < jnums[i] > 2*nums[j] 我们就将 (i, j) 称作一个重要翻转对

你需要返回给定数组中的重要翻转对的数量。

示例 1:

**输入**: [1,3,2,3,1]
**输出**: 2

示例 2:

**输入**: [2,4,3,5,1]
**输出**: 3

2.思路

分治过程中,我们可以顺便统计翻转对的数量,统计跨越左右子数组的翻转对

在合并左右子数组之前,我们需要找出那些一个元素在左子数组,另一个元素在右子数组的翻转对。具体做法是,使用两个指针分别遍历左右子数组,如果左子数组的当前元素 nums[i] 大于右子数组当前元素 nums[j] 的两倍,那就说明从 i 到左子数组末尾的所有元素都和 nums[j] 构成翻转对,我们把这些翻转对的数量累加到总的计数中,然后右子数组的指针 j 向后移动一位;如果不满足条件,就把左子数组的指针 i 向后移动一位。

把左右子数组合并成一个有序的数组。在合并的过程中,我们比较左右子数组的元素大小,将较小的元素依次放入临时数组中,直到左右子数组的元素都处理完。最后,把临时数组中的元素复制回原数组。

3.代码

#include <vector>

class Solution {
public:
    // 归并排序函数,用于统计数组中翻转对的数量
    // nums 是待处理的数组
    // left 是当前处理区间的左边界
    // right 是当前处理区间的右边界
    // tmp 是用于辅助归并操作的临时数组
    int MergeSort(std::vector<int>& nums, int left, int right, std::vector<int>& tmp) {
        // 递归终止条件:如果左边界大于等于右边界,说明当前区间为空或者只有一个元素,不存在翻转对,返回 0
        if (left >= right)
            return 0;

        // 计算当前区间的中间位置,用于将数组划分为左右两个子数组
        int mid = left + (right - left) / 2;

        // 递归调用 MergeSort 函数,分别计算左子数组和右子数组中的翻转对数量
        // 并将它们的结果累加到 count 中
        int count = 
            MergeSort(nums, left, mid, tmp) + 
            MergeSort(nums, mid + 1, right, tmp);

        // 统计跨越左右子数组的翻转对数量
        int left1 = left, right1 = mid;
        int left2 = mid + 1, right2 = right;
        // 遍历左右子数组,寻找满足翻转对条件的元素对
        while (left1 <= right1 && left2 <= right2) {
            // 为了避免乘法运算时出现整数溢出,将 nums[left1] 转换为 long long 类型
            if ((long long)nums[left1] > 2LL * nums[left2]) {
                // 如果满足 nums[left1] > 2 * nums[left2],说明从 left1 到 right1 的所有元素都与 nums[left2] 构成翻转对
                // 因此将这些翻转对的数量(right1 - left1 + 1)累加到 count 中
                count += right1 - left1 + 1;
                // 右子数组指针后移,继续检查下一个元素
                left2++;
            } else {
                // 不满足条件,左子数组指针后移,继续检查下一个元素
                left1++;
            }
        }

        // 重置指针,准备进行归并操作,将左右子数组合并为一个有序数组
        left1 = left, right1 = mid;
        left2 = mid + 1, right2 = right;
        // 记录临时数组的当前索引
        int index = left;
        // 比较左右子数组的元素,将较小的元素依次放入临时数组中
        while (left1 <= right1 && left2 <= right2) {
            if (nums[left1] <= nums[left2]) {
                // 左子数组的当前元素较小,将其放入临时数组,并移动左子数组指针和临时数组索引
                tmp[index++] = nums[left1++];
            } else {
                // 右子数组的当前元素较小,将其放入临时数组,并移动右子数组指针和临时数组索引
                tmp[index++] = nums[left2++];
            }
        }

        // 处理左子数组中剩余的元素,将它们依次放入临时数组
        while (left1 <= right1) {
            tmp[index++] = nums[left1++];
        }

        // 处理右子数组中剩余的元素,将它们依次放入临时数组
        while (left2 <= right2) {
            tmp[index++] = nums[left2++];
        }

        // 将临时数组中的元素复制回原数组,使原数组在当前区间内有序
        for (int i = left; i <= right; ++i) {
            nums[i] = tmp[i];
        }

        // 返回当前区间内的翻转对总数
        return count;
    }

    // 计算数组中翻转对的总数
    // nums 是待处理的数组
    int reversePairs(std::vector<int>& nums) {
        // 创建一个与原数组大小相同的临时数组,用于辅助归并操作
        std::vector<int> tmp(nums.size());
        // 调用 MergeSort 函数,对整个数组进行处理,并返回翻转对的总数
        return MergeSort(nums, 0, nums.size() - 1, tmp);
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

penguin_bark

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

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

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

打赏作者

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

抵扣说明:

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

余额充值