【每日一题】LeetCode. 计算右侧小于当前元素的个数

每日一题,防止痴呆 = =

一、题目大意

给定一个整数数组 nums,按要求返回一个新数组 counts。数组 counts 有该性质: counts[i] 的值是 nums[i] 右侧小于 nums[i] 的元素的数量。
【每日一题】LeetCode. 计算右侧小于当前元素的个数_第1张图片
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/count-of-smaller-numbers-after-self

二、题目思路以及AC代码

思路:树状数组

这道题暴力的方法没有试,应该就是O(N^2)的复杂度,虽然这题没有给数组长度范围,但很明显是不应该直接暴力做的。

其实没做出这道题的原因还是对树状数组不够熟悉,这道题感觉比较自然的想法是,从后往前遍历,此时当我们遍历到某个数的时候,只需要考虑已经遍历过的数比它小的有多少个,自然就是其右侧比它小的数量了。我相信如果对树状数组熟悉的人肯定很自然的就想到的解法。

我们可以维护一个树状数组,用来动态的求解桶数组的前缀和,由于树状数组的求和和更新都是O(logN)的复杂度,所以整体是O(NlogN)的复杂度,比O(N^2)快了不少。举个例子来说明求解过程。

对于示例数组:[5, 2, 6, 1]

首先我们遍历 1,桶数组 count[1]++,以O(logN)的复杂度更新树状数组以及求前缀和 (count[0]) 为0.

然后遍历6,桶数组 count[6]++,O(logN)的复杂度更新树状数组以及求前缀和 (count[0] + count[1] + … + count[5]) 为1.

然后遍历2,桶数组 count[2]++,O(logN)的复杂度更新树状数组以及求前缀和 (count[0] + count[1] + count[2]) 为1.

最后遍历5,桶数组 count[5]++,O(logN)的复杂度更新树状数组以及求前缀和 (count[0] + count[1] + … + count[4]) 为2.

最终求得正确结果。

关于树状数组的原理以及知识,我之前有个博客讲解过,不过是转载的,这次也是又复习了一遍。https://blog.csdn.net/m0_38055352/article/details/105726733

然后还有一点,这里由于我们不知道数组整数的范围,所以桶数组的大小可能会很大,但是由于这里我们只需要大小关系,而对于值没有要求,所以我们可以将桶数组压缩,相当于是按照大小关系Hash了一下,就可以把长度限制为和数组大小一样了。

AC代码
class Solution {
     
private:
    vector<int> a;
    vector<int> c;

    void init(int length) {
     
        c.resize(length, 0);
    }

    int lowbit(int x) {
     
        return x & -x;
    }

    void update(int i, int val) {
     
        while (i < c.size()) {
     
            c[i] += val;
            i += lowbit(i);
        }
    }

    int getSum(int i) {
     
        int res = 0;
        while (i > 0) {
     
            res += c[i];
            i -= lowbit(i);
        }

        return res;
    }

    void Discretization(vector<int> nums) {
     
        a.assign(nums.begin(), nums.end());
        sort(a.begin(), a.end());
        a.erase(unique(a.begin(), a.end()), a.end());
    }

    int getId(int x) {
     
        return lower_bound(a.begin(), a.end(), x) - a.begin() + 1;
    }
public:
    vector<int> countSmaller(vector<int>& nums) {
     
        int n_size = nums.size();
        vector<int> res;
        if (!n_size) return res;

        Discretization(nums);
        n_size = nums.size();

        init(n_size + 5);

        for (int i=n_size-1;i>=0;i--) {
     
            int idx = getId(nums[i]);
            update(idx, 1);
            res.push_back(getSum(idx - 1));
        }

        reverse(res.begin(), res.end());
        return res;
    }
};

如果有问题,欢迎大家指正!!!

你可能感兴趣的:(每日一题,leetcode,数据结构,算法,树状数组)