LeetCode Top Interview Questions 315. Count of Smaller Numbers After Self (Java版; Hard)

welcome to my blog

LeetCode Top Interview Questions 315. Count of Smaller Numbers After Self (Java版; Hard)

题目描述
You are given an integer array nums and you have to return a new counts array. The counts array has the property where counts[i] is the number of smaller elements to the right of nums[i].

Example:

Input: [5,2,6,1]
Output: [2,1,1,0] 
Explanation:
To the right of 5 there are 2 smaller elements (2 and 1).
To the right of 2 there is only 1 smaller element (1).
To the right of 6 there is 1 smaller element (1).
To the right of 1 there is 0 smaller element.
class Solution {
    public List<Integer> countSmaller(int[] nums) {
        List<Integer> list = new ArrayList<>();
        int n = nums.length;
        if(n==0){
            return list;
        }
        //对索引进行归并排序, 根据索引对应的值的大小进行条件划分
        int[] index = new int[n];
        for(int i=0; i<n; i++){
            index[i] = i;
            list.add(0);
        }
        //
        mergeSort(list, nums, index, 0, n-1);
        return list;
        
    }

    private void mergeSort(List<Integer> list, int[] nums, int[] index, int L, int R){
        if(L>=R){
            return;
        }
        int mid = L + (R - L)/2;
        mergeSort(list, nums, index, L, mid);
        mergeSort(list, nums, index, mid+1, R);
        merge(list, nums, index, L, mid, R);
    }

    private void merge(List<Integer> list, int[] nums, int[] index, int L, int mid, int R){
        int[] help = new int[R-L+1];
        int p1=L, p2=mid+1, p=0;
        int count=0;
        while(p1<=mid && p2<=R){
            //右部分小
            if(nums[index[p1]] > nums[index[p2]]){
                help[p++] = index[p2++];
                count++;
            //左部分小
            }else{
                help[p++] = index[p1];
                //
                list.set(index[p1], list.get(index[p1])+count);
                p1++;
            }
        }
        while(p1<=mid){
            help[p++] = index[p1];
            list.set(index[p1], list.get(index[p1])+count);
            p1++;
        }
        while(p2<=R){
            help[p++] = index[p2++];
        }
        for(int i=0; i<help.length; i++){
            index[L+i] = help[i];
        }

    }
}
第一次做, 只能想到暴力的方法, 于是直接看的题解, 明白了思想后自己便能写出来了, 用的是归并排序的思想, 之前做过类似的题, 求的是逆序对的数量, 这道题更难一些, 因为并不是对nums进行归并排序, 而是对indexMap进行归并排序. indexMap记录的是原始索引所在的位置的信息. 归并排序中根据nums中元素的大小分成两种情况; 核心: 归并排序会把数组分成两部分处理, 右部分小的时候count++, 左部分小的时候更新对应的res. 在纸上写个例子模拟一遍流程就清楚了, 比如[5,2,6,1]; 细节: 单独需要注意的是mergesort中的if条件句是left>=right, 因为会有left>right的情况, 当nums.length==0时, left=0, right=-1, 所以要么在input check中检查length, 要么把这个if条件改成left>=right
class Solution {
    public List<Integer> countSmaller(int[] nums) {
        List<Integer> result = new ArrayList<>();
        if(nums==null)
            return result;
        //
        //索引映射, 原始索引与新索引的对应关系, 新索引会改变
        int n = nums.length;
        int[] indexMap = new int[n];
        for(int i=0; i<n; i++){
            indexMap[i] = i;
        }
        //临时结果
        int[] res = new int[n];
        //对indexMap进行归并排序
        mergesort(res, nums, indexMap, 0, n-1);
        //把res的结果给result
        for(int a : res){
            result.add(a);
        }
        return result;
        
    }
    public void mergesort(int[] res, int[] nums, int[] indexMap, int left, int right){
        //会有left>right的情况吗? 有, 当nums.length==0时, left=0, right=-1, 所以要么在input check中检查length, 要么把这个if条件改成left>=right
        if(left>=right)
            return;
        int mid = left + ((right-left)>>1);
        mergesort(res, nums, indexMap, left, mid);
        mergesort(res, nums, indexMap, mid+1, right);
        merge(res,nums,indexMap,left,mid,right);
    }
    public void merge(int[] res, int[] nums, int[] indexMap, int left, int mid, int right){
        int[] help = new int[right-left+1];
        int p1 = left, p2 = mid+1, p=0;
        int count = 0;
        while(p1<=mid && p2<=right){
            //如果右部分的小
            if(nums[indexMap[p1]] > nums[indexMap[p2]]){
                help[p++] = indexMap[p2++];
                count++;
            }
            //如果左部分的小
            else{
                help[p++] = indexMap[p1];
                res[indexMap[p1++]] += count;
            }
        }
        //如果左部分还有没处理的
        while(p1<=mid){
            help[p++] = indexMap[p1];
            res[indexMap[p1++]] += count;
        }
        //如果右部分还有没处理的
        while(p2<=right){
            help[p++] = indexMap[p2++];
            //此时不需要再更新count了
        }
        //将help的结果更新给indexMap
        for(int i=0; i<right-left+1; i++){
            indexMap[left+i] = help[i];
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值