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

题目

给定一个整数数组 nums,按要求返回一个新数组 counts。数组 counts 有该性质: counts[i] 的值是 nums[i] 右侧小于 nums[i] 的元素的数量。
链接:https://leetcode-cn.com/problems/count-of-smaller-numbers-after-self

示例

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

思路

1.暴力法

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var countSmaller = function(nums) {
     
    let len = nums.length;
    let res = new Array(len).fill(0);
    for(let i = len -1; i>=0;--i){
     
        for(let j = i+1; j<len;++j){
     
            if(nums[i]>nums[j]){
     
                res[i]++;
            }
        }
    }
    return res;
};

从最后一个元素开始遍历,比较他和后面元素的大小,并记录,但这个会超时,不能ac

2. 遍历后的数组排序

因为每个元素的遍历都要,和之后的所有元素进行比较,那如果对后面的元素进行排序,当遇到比该元素小的值时,停止比较,记录一下之后元素的个数。代码如下

/**
 * @param {number[]} nums
 * @return {number[]}
 */
function ListNode(value){
     
    this.val = value;
    this.next = null
    this.length = 1;
}

var countSmaller = function(nums) {
     
    let len = nums.length;
    let head = new ListNode(-1);
    let res = new Array(len).fill(0);
    for(let i = len -1; i>=0;--i){
     
        let pre = head;
        while(pre.next && pre.next.val>=nums[i]){
     
            pre.next.length++;
            pre = pre.next;
        }
        let node = new ListNode(nums[i]);
        node.length = pre.next?pre.next.length+1:0;
        node.next = pre.next;
        pre.next = node;
        res[i] = node.length;
    }
    return res;
};

这个结果是可以ac的,但时间效率不高,本质还是O(n^2)。
LeetCode 315. 计算右侧小于当前元素的个数_第1张图片

3. 归并

其实这道题和另一道求逆序对个数的题很像,但那道题是求逆序对的总数,而这道题是求每个位置开始的逆序对,一开始也是想用归并做的,但考虑到归并的时候会是数组的索引会变,如果要新增了一个索引数组作为映射,貌似也很麻烦,所以就没往归并的方向做下去了。 但看题解的时候,发现可以归并一个索引数组,代码如下:

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var countSmaller = function(nums) {
     
    let len = nums.length;
    let arr_index = new Array(len);
    nums.forEach((_,key)=>{
     
        arr_index[key] = key;
    })
    let ans = new Array(len).fill(0);
    const merge = (left, right)=>{
     
        let llen = left.length;
        let rlen = right.length;
        let len = llen+rlen;
        let res = new Array(len);
        for(let i = 0, j= 0, k= 0 ; k <len;++k){
     
            if(i == llen){
     
                res[k] = right[j++]
            }else if(j == rlen){
     
                res[k] = left[i++];
            }else if(nums[left[i]]>nums[right[j]]){
     
                ans[left[i]]+=(rlen-j);
                res[k] = left[i++];
            }else{
     
                res[k] = right[j++];
            }
        }
        return res;
    }

    const mergeSort = (arr)=>{
     
        if(arr.length<2){
     
            return arr;
        }
        let len = arr.length;
        let mid = len>>1;
        let left = arr.slice(0,mid);
        let right = arr.slice(mid);
        return merge(mergeSort(left), mergeSort(right));
    }
    mergeSort(arr_index);
    return ans;
};

LeetCode 315. 计算右侧小于当前元素的个数_第2张图片

你可能感兴趣的:(Leetcode,算法,leetcode)