归并排序+分治+计算逆序对 315. 计算右侧小于当前元素的个数

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 个更小的元素.

解题
类似计算逆序对:
对每个数都计算逆序对,并保存;
用index数组保存每个数原本的下标;
其逆序对结果保存在res[i]中;
最后的排序为对index的排序,不改变原数组;

错误解:
错误点:归并时遇到相等的数值直接跳过,导致少加错误;

class Solution {
     
public:
    vector<int> countSmaller(vector<int>& nums) {
     
        int n=nums.size();
        res.resize(n,0);
        index.resize(n);
        tmp.resize(n,0);
        for(int i=0;i<n;i++) index[i]=i;
        //index装着下标
        merge_sort(nums,0,n-1);
        return res;
    }
private:
    vector<int> tmp;
    vector<int> index;
    vector<int> res;
    void merge(vector<int> &nums,int start,int mid,int end){
     
        int left=mid+1;
        int begin=start;
        int t=start;
        while(start<=mid&&left<=end){
     
            if(nums[index[start]]<nums[index[left]]){
     
                tmp[t++]=index[start];
                res[index[start]]+=left-mid-1;  //该位置的下标需要变化
                start++;
                }
            else if(nums[index[start]]==nums[index[left]]) tmp[t++]=index[start++];  //相等的话不加
            else    tmp[t++]=index[left++];
                //右边小说明左边的都大
        }
        while(start<=mid) {
     
            tmp[t++]=index[start];  
            res[index[start]]+=end-mid;  //该位置的下标需要变化
            start++;
        }
        while(left<=end) tmp[t++]=index[left++];
        for(t=begin;t<=end;t++) index[t]=tmp[t];
    }
    void merge_sort(vector<int>&nums,int left,int right){
     
        if(left>=right) return;
        
        int mid=left+(right-left)/2;  //偏左  
        merge_sort(nums,left,mid);
        merge_sort(nums,mid+1,right);
        merge(nums,left,mid,right);  //左闭右闭 left~mid  mid+1~right
        return;
    }
};

正确解
归并时,若左边与右边数相等,则左边数开始遍历下一个,将当前比自身小的加入结果;

class Solution {
     
public:
    vector<int> countSmaller(vector<int>& nums) {
     
        int n=nums.size();
        res.resize(n,0);
        index.resize(n);
        tmp.resize(n,0);
        for(int i=0;i<n;i++) index[i]=i;
        //index装着下标
        merge_sort(nums,0,n-1);
        return res;
    }
private:
    vector<int> tmp;
    vector<int> index;
    vector<int> res;
    void merge(vector<int> &nums,int start,int mid,int end){
     
        int left=mid+1;
        int begin=start;
        int t=start;
        while(start<=mid&&left<=end){
     
            // if(begin==0&&end==39&&mid==19){
     
            //     cout<
            // }
            if(nums[index[start]]<=nums[index[left]]){
     
                tmp[t++]=index[start];
                res[index[start]]+=left-mid-1;  //该位置的下标需要变化
                start++;
                }
            //else if(nums[index[start]]==nums[index[left]]) tmp[t++]=index[start++];  //相等的话不加
            else    tmp[t++]=index[left++];
                //右边小说明左边的都大
        }
        while(start<=mid) {
     
            tmp[t++]=index[start];  
            res[index[start]]+=end-mid;  //该位置的下标需要变化
            start++;
        }
        while(left<=end) tmp[t++]=index[left++];
        for(t=begin;t<=end;t++) index[t]=tmp[t];
    }
    void merge_sort(vector<int>&nums,int left,int right){
     
        if(left>=right) return;
        int mid=left+(right-left)/2;  //偏左  
        merge_sort(nums,left,mid);
        merge_sort(nums,mid+1,right);
        merge(nums,left,mid,right);  //左闭右闭 left~mid  mid+1~right
        return;
    }
};

你可能感兴趣的:(leetcode,分治)