[路飞]_程序员必刷力扣题: 315. 计算右侧小于当前元素的个数

144 阅读2分钟

「这是我参与2022首次更文挑战的第5天,活动详情查看:2022首次更文挑战

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

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

示例 1:

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

示例 2:

输入: nums = [-1]
输出: [0]

示例 3:

入: nums = [-1,-1]
输出: [0,0]

提示:

  • 1 <= nums.length <= 105
  • -104 <= nums[i] <= 104

暴力求解

思路

直接双循环,大佬们肯定都知道,我们就略过

归并排序

思路 由题可得我们这道题目要求数组中每一位元素的右侧所有元素中,比当前元素值小的元素的个数,正如示例中所示

我们会发现先这个题目跟剑指 Offer 51. 数组中的逆序对非常相似

逆序对:求所有逆序对的个数 当前:求右侧小于当前元素的个数

本质上是同一个道理,只是本题中需要区分出来每个值构成的逆序对的数量,而不是所有逆序对的总数

这里我们采用归并排序的方式。

为什么用归并排序? 因为数组一旦有序,那么就不需要逐一遍历了,而是可以找到一个临界点,直接计算求解

假设当前有两个有序数组left[1,3,5] rightp[2,4,6]

因为1小于2,所以1右侧小于它的值的数量直接为0

因为3大于1小于4,所以3右侧所有小于它的值的数量直接等于1(4的下标1)

因为5大于4小于6,所以4右侧所有小于它的值的数量直接等于2(6的下标2)

这样就大大降低了时间复杂度(相比暴力求解)

实现方式

我们采用归并排序的方式,每次会将数组从中间均分,分别得到两个有序的数组

我们声明i指针用来遍历sortLeft,用p指向sortRight的首位

遍历sortLeft的值,没一个值与右侧的sortRight[p]进行比较,如果右侧的值比较小则p++

p的下标值就是sortRight中小于left[i]的数量,所以left[i]右侧小于它的值+=p

我们怎么记录原数组中每一个值右侧的数量呢?排序打乱数组顺序怎么记得原来的顺序呢

  • 这里我们直接把每个值变成一个对象,{val:值,res:0}
  • 也可以用一个数组来记录原来数组中的位置,与数组一起进行排序 这样在排序的时候我们就可以吧每个值的结果+=在他的res中

最后从数组中取出res即可

var countSmaller = function (nums) {
    var i = 0
    while (i < nums.length) {
        nums[i] = {val:nums[i],res:0}
        i++
    }
    
    function computedNum(nums) {
        if (nums.length < 2) return nums
        var len = nums.length
        var mid = len >> 1
        var left = nums.slice(0, mid)
        var right = nums.slice(mid)
        var sortLeft = computedNum(left)
        var sortRight = computedNum(right)
        // 开始统计right中的小于left的值
        var i = 0
        var p = 0
        while (i < left.length) {
            var item = sortLeft[i]
            while (sortRight[p] && item.val > sortRight[p].val && p < sortRight.length) p++
            item.res += p
            i++
        }
        // 合并数组

        var arr = Array(nums.length)
        var index = 0;
        var p = q = 0
        while (index < nums.length) {
            if (p === sortLeft.length) {
                arr[index++] = sortRight[q++]
            } else if (q === sortRight.length) {
                arr[index++] = sortLeft[p++]
            } else if (sortLeft[p].val < sortRight[q].val) {
                arr[index++] = sortLeft[p++]
            } else {
                arr[index++] = sortRight[q++]
            }
        }
        return arr
    }
    computedNum(nums)
    var j = 0
    while (j < nums.length) {
        nums[j] = nums[j].res
        j++
    }
    return nums


};