LeetCode: 315. 计算右侧小于当前元素的个数

给定一个整数数组 nums,按要求返回一个新数组 counts。数组 counts 有该性质: counts[i] 的值是  nums[i] 右侧小于 nums[i] 的元素的数量。

示例:

输入: [5,2,6,1]
输出: [2,1,1,0] 
解释:
5 的右侧有 2 个更小的元素 (2 和 1).
2 的右侧仅有 1 个更小的元素 (1).
6 的右侧有 1 个更小的元素 (1).
1 的右侧有 0 个更小的元素.

分析:

       这个题实际上就是求解各个逆序对,而如何求解逆序对,已经遇到过:数组中的逆序对,采用的是分治的方法。与这道题不同的是,本题需要分别求出各数的逆序对。

       为了解决这个问题,可以采用“索引数组”的方法。如果元素在算法的执行过程中位置发生变化,而我们还想要定位它原来的位置,那么可以使用索引数组。技巧在于用原始数组的元素来比较大小,而不改变原始数组的顺序,真正发生位置变化的是索引数组中的相对顺序。这样我们就可以利用索引来定位了。

class Solution {
public:
    void merge(int left, int mid, int right, vector<int> &nums, vector<int> &index, vector<int> &temp, vector<int> &res){
        int i = left, j = mid + 1, k = left;
        while(i <= mid && j <= right){
            if(nums[index[i]] <= nums[index[j]]){
                temp[k++] = index[i];
                res[index[i]] += j - mid - 1;
                i++;
            }else{
                temp[k++] = index[j++];
            }
        }
        while(i <= mid){
            temp[k++] = index[i];
            res[index[i]] += j - mid - 1;
            i++;
        }
        while(j <= right){
            temp[k++] = index[j++];
        }
        for(int i = left; i <= right; i++) index[i] = temp[i];
    }

    void mergeSort(int left, int right, vector<int> &nums, vector<int> &index, vector<int> &temp, vector<int> &res){
        if(left < right){
            int mid = (left + right) / 2;
            mergeSort(left, mid, nums, index, temp, res);
            mergeSort(mid + 1, right, nums, index, temp, res);
            merge(left, mid, right, nums, index, temp, res);
        }
    }

    vector<int> countSmaller(vector<int>& nums) {
        int n = nums.size();
        if(n == 0) return {};
        vector<int> index(n);
        vector<int> temp(n);
        vector<int> res(n, 0);
        for(int i = 0; i < n; i++) index[i] = i;
        mergeSort(0, n - 1, nums, index, temp, res);
        return res;
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值