力扣刷题——315. 计算右侧小于当前元素的个数

题目:

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

示例 1:

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

方法一:暴力循环(超时)

        所谓计算counts数组,即求每一个数字右侧比他小的个数。首先我们想到的就是暴力循环。下面是暴力循环的代码。虽然暴力循环的代码较为简洁但时间复杂度为O(n²),根据数据长度会超时。

class Solution {
public:
    vector<int> countSmaller(vector<int>& nums) {
        vector<int>ans(nums.size());
        for(int i = 0;i < nums.size();i++){//暴力循环
            for(int j = i + 1;j < nums.size();j++){
                if(nums[j] < nums[i]){
                    ans[i]++;
                }
            }
        }
        return ans;
    }
};

​

方法二:利用归并排序进行计算

首先,我们需要了解以下什么是归并排序,我们先看下面代码帮助理解:

#include<iostream>
#include <vector>
using namespace std;
#define C_MAX 10010

class Solution {
public:
    int tem[C_MAX];
    void mergesort(vector<int>&nums,int left,int right){
        int mid = left + (right - left) / 2;
        mergesort(nums,left,mid);
        mergesort(nums,mid + 1,right);//进行递归
        int i = 0;
        int count1 = left;
        int count2 = mid + 1;
        while(count1 <= mid && count2 <= right){//对两边进行判断大小然后入队
            if(nums[count1] <= nums[count2]){
                tem[i++] = nums[count1++];
            }
            else{
                tem[i++] = nums[count2++];
            }
        }
        while(count1 <= mid){
            tem[i++] = nums[count1++];
        }
        while(count2 <= right){
            tem[i++] = nums[count2++];
        }
    }
};

以上代码就是归并排序的过程,我们也可以通过下面的图来加深理解(图来自力扣-flix)

 在归并排序的时候,利用上图,我们可以看出逆序对的个数,把一个数组分成两个部分分别进行递归排序,在合成的时候,我们需要按照其大小进行排序合成,但图中画圈的地方(按照两个部分的前后,若前面部分有数字大于后面数字即存在逆序对数,例如,65数组分为l1[6],l2[5],以为本身l1就在l2的前面,并且6>5,即存在一个逆序对数),可以明显的计算出逆序对数。

 

 但这样,我们只能计算逆序对数,无法记录每个数的逆序对数。我们可以通过使用一个索引数组跟着一起排序,就可以知道该数字的在原始数组的下标。便可以记录该数字的逆序对数。

class Solution {
public:
    int tem1[100100];   //数组1用来临时记录数组
    int tem2[100100];   //数组2用来临时记录索引
    void mergesort(vector<int>&nums,vector<int>&index,vector<int>&ans,int l,int r){
        if(l >= r){
            return;
        }
        int mid = l + (r - l) / 2;
        int count1 = l;
        int count2 = mid + 1;
        mergesort(nums,index,ans,l,mid);
        mergesort(nums,index,ans,mid + 1,r);    //分别递归进行归并排序。
        int i = 0;
        while(count1 <= mid && count2 <= r){
            if(nums[count1] <= nums[count2]){
                ans[index[count1]] += count2 - mid - 1;   //这里的计算是用下面的图来解释                        
                tem1[i] = nums[count1];                   
                tem2[i++] = index[count1++];        //始终保持下标和原数组一致
            }
            else{
                tem1[i] = nums[count2];
                tem2[i++] = index[count2++];
            }
        }
        while(count1 <= mid){
            ans[index[count1]] += count2 - mid - 1;
            tem1[i] = nums[count1];
            tem2[i++] = index[count1++];           
        }
        while(count2 <= r){
            tem1[i] = nums[count2];
            tem2[i++] = index[count2++];
        }
        for(int i = l;i <= r;i++){
            index[i] = tem2[i - l];
            nums[i] = tem1[i - l];            //上面与归并排序基本类似
        }

    }
    vector<int> countSmaller(vector<int>& nums) {
        int n = nums.size();
        vector<int>ans(n);
        vector<int>index(n);
        for(int i = 0;i < n;i++){
            index[i] = i;
        }
        mergesort(nums,index,ans,0,n - 1);
        return ans;
    }
};

计算逆序对数的技巧:

对于1的时候, 右边没有比小的故跳过。3的时候,右边的指针指到4,所以4前面的个数就是比3小的个数,3的逆序对数为1。5的时候,右边指针指到7,故5的逆序对数为2,6亦是如此。(因为我们使用index数组,保持数字仍然与原数组下标一致,故可以直接计算)。

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值