leetcode 315 逆序数(count of smaller numbers after self)——分治

本文介绍了一种利用分治法优化逆序数计算的方法,通过将数组分为两部分,分别排序后再合并,计算出每个元素后面有多少个比它小的数,即逆序数,采用此方法的时间复杂度为O(N*logN)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:

 

思路: 

(1)最先能想到的当然是遍历。这样使用两个循环,第一个循环遍历每一个数,第二个循环遍历这个数后面的所有数,找有几个是比它小的。这种时间复杂度是O(N^2)

(2)本题使用了分治的思路,复杂度是O(N*logN)

应用合并排序。如果我们对数组进行排序,那么对于某个特定的数据,其后面的逆序数等于在排序过程中需要移动到该数前面的个数。时间复杂度O(nlogn)。

分治的知识见下图

shbi 

 

上面讲的是排序的一个例子。

原本是排序{5,-7,9,8,1,4,-3,10,2,0} 按照mid(mid=begin+end  /2)的观念不断的划分子问题

到不能继续划分的{5}{-7} {9} {8} {1}等等的时候,两两合并

 

看上面的图,本题的主要思路:

假设两个有序(升序)数组进行归并时候,怎么计算count[i]呢(count表示后面有多少个数比nums[i]小)

(1)首先nums[ i_0]=-7,nums[ j_0]=-2,所以-7后面没有比它小的数,count[i_0]不变,i到达i_1的位置, j位置不变

(2)此时nums[i_1]=1,nums[j_0]=-2,所以count[i_1]++, j到达j_1位置(注明一点,开头已经说了有序,所以在一个子数组里面不可能有数比前面的数小)nums[j_1]=1

此时相等怎么办呢,count不用加就好了

……然后后面依次是这么计算count数组的

还有一个问题,就是如何绑定count和nums呢,因为我们最后输出的count是按照nums的数组的数的顺序来的 

我们定义数组pos[i] = j,表示第排名i个数据的元素下标是j。i就是排序完之后的数组的顺序,pos[i]记录的是这个数在nums数组的下标,这样最后输出就能按照题目的格式要求了。

java代码

 最后返回的result就是count的意思,from to 就是begin end 的意思,m就是mid的意思

public class Solution {
    private void sort(int[] nums, int[] smaller, int[] pos, int from, int to) {
        if (from >= to) return;
        int m = (from + to) / 2;
        sort(nums, smaller, pos, from, m);//排序左侧
        sort(nums, smaller, pos, m+1, to);//排序右侧
        int[] merged = new int[to-from+1];//合并,smaller是最后的count数组
        int i=from, j=m+1, k=0, jump = 0;//jump跳了几次,就说明后面有几个数比它小
        while (i<=m || j<=to) {//能合并 
            if (i>m) {
//前面的数组没数了,把后面排好序的数组的数直接加进来
                jump ++;//事实证明这句话是废话,不加也ok,加上只是为了说明后面的数跳一个加一下
                merged[k++] = pos[j++];//merged[k]=pos[j],k++,j++
            } else if (j>to) {
//后面数组没数了
                smaller[pos[i]] += jump;//pos[i]是当前数的原始位置
                merged[k++] = pos[i++];
            } else if (nums[pos[i]] <= nums[pos[j]]) {
                smaller[pos[i]] += jump;
                merged[k++] = pos[i++];
            } else {
                jump ++;
                merged[k++] = pos[j++];
            }
        }
//合并完之后,smaller数组已经记录好了原来顺序的数的count,
        for(int p=0; p<merged.length; p++) pos[from+p] = merged[p];
    }
    public List<Integer> countSmaller(int[] nums) {
        int[] smaller = new int[nums.length];
        int[] pos =new int[nums.length];
        for(int i=0; i<pos.length; i++) pos[i] = i;//先记录原先数的下标
        sort(nums, smaller, pos, 0, nums.length-1);
        List<Integer> result = new ArrayList<>(nums.length);
        for(int i=0; i<nums.length; i++) result.add(smaller[i]);
        return result;
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值