leetcode 315. 计算右侧小于当前元素的个数(hard)【小林优质解法】

news2025/1/16 20:15:09

链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

代码:

class Solution {
    int[]counts;    //用来存储结果
    int[]index; //用来绑定数据和原下标
    int[]helpNums;  //用于辅助排序 nums 数组
    int[]helpIndex; //用于辅助排序 index 数组

    public List<Integer> countSmaller(int[] nums) {
        List<Integer> list=new ArrayList<>();
        int length=nums.length;
        counts=new int[length];
        index=new int[length];
        helpNums=new int[length];
        helpIndex=new int[length];

        //初始化 index 数组
        for(int i=0;i<length;i++){
            index[i]=i;
        }

        mergeSort(nums,0,length-1);

        for(int count:counts){
            list.add(count);
        }

        return list;
    }

    //统计 nums 数组中 left ~ right 区间中的逆序对,将统计的数据保存到 counts 中
    public void mergeSort(int[] nums,int left,int right){
        //递归出口
        if(left>=right){
            return;
        }

        //int mid=(right-left)/2+left;    //获取中点
        int mid=(left+right)/2;
        mergeSort(nums,left,mid);   //统计左区间的所有逆序对
        mergeSort(nums,mid+1,right);   //统计右区间的所有逆序对

        //统计左边一个数据,右边一个数据构成的逆序对
        int cur1=left,cur2=mid+1,i=0;
        while(cur1<=mid&&cur2<=right){
            //统计逆序对
            if(nums[cur1]>nums[cur2]){
                counts[index[cur1]]+=right-cur2+1;
                //将 nums 数组和 index 数组同步修改
                helpNums[i]=nums[cur1];
                helpIndex[i++]=index[cur1++];
            }else{
                helpNums[i]=nums[cur2];
                helpIndex[i++]=index[cur2++];
            }
        }

        //处理没有遍历到的数据
        while(cur1<=mid){
            helpNums[i]=nums[cur1];
            helpIndex[i++]=index[cur1++];
        }

        while(cur2<=right){
            helpNums[i]=nums[cur2];
            helpIndex[i++]=index[cur2++];
        }

        //用辅助数组中的数据去代替原数组中的数据
        for(int j=left;j<=right;j++){
            nums[j]=helpNums[j-left];
            index[j]=helpIndex[j-left];
        }
    }
}

题解:

        本题和博主写过的另一题的解法几乎一样,推荐看leetcode LCR 170. 交易逆序对的总数(hard)【小林优质解法】

        首先我们来分析一下题意,题目要求我们统计每个数据右边有多少数据小于当前数据,还是比较好理解的,学过线性代数的同学都知道,对于 5,2 这种左边大于右边的数,我们称之为逆序对,所以题目就是要求我们统计数组中每个数据可以组成的逆序对个数

        这道题很容易想出暴力解法,就是遍历出数组中所有两个数的组合,判断其中有多少逆序对即可,但时间复杂度为 O(n^2),是比较糟糕的,在本题中会超时

        现在博主来介绍如何通过归并排序来解决该题,博主就默认大家是很理解归并排序的,要是不理解一定要先理解了归并排序才能看懂下面的内容,推荐看归并排序(递归)

        题目要求统计逆序对个数,我们可以将数组分为左右两个区间,先统计左区间的逆序对个数 A,再统计右区间的逆序对个数 B,最后统计左区间取一个数据,右区间取一个数据组成的逆序对的个数 C,通过 A+B+C 便能得到数组中所有的逆序对个数,其中,统计左区间的逆序对个数 A 和右区间的逆序对个数 B 与统计整个数组的逆序对相同,所以是递归操作,于是我们的目光就要集中在统计左区间取一个数据,右区间取一个数据组成的逆序对的个数 C 上

        我们统计 C 的个数时是在统计完 A 和 B 之后,根据归并排序的操作,此时已经排序好了左边和右边区间的数据,归并排序按照递减排序,如下图:

        用 cur1 和 cur2 遍历左边和右边的区间,找到其中的逆序对

        (1).nums[ cur1 ] > nums[ cur2 ] ,由于在左右区间数据都是递减的,所以 cur2 后面的数据都小于 cur1 指向的数据,此时我们就得到了以 cur1 指向的数据为首的一批逆序对,个数为 right - cur2 + 1 ,要将 nums[ cur1 ] 这个数据的逆序对个数添加到 counts 数组中

        现在就有一个问题,counts 数组对应的是 nums[ cur1 ] 这个数据的原下标,因为数据经过了归并排序,顺序已经打乱了,cur1 不是该数据的原下标,我们如何知道 nums[ cur1 ] 这个数据的原下标呢?这里就需要一个小技巧,我们可以在一开始的时候就创建一个数组 index ,index 数组记录了 nums 数组中每个数据的原下标,如下所示:

nums:5        2        6        1

index:0        1        2        3

        在这之后,nums 数组如何改变。index 数组就如何改变,这样处理后无论数组中的数据如何改变,我们都能知道每个数据对应的原下标是多少,所以得到 nums[ cur1 ] 这个数据的一批逆序对数目后,要执行 counts[ index[ cur1 ] ] + = right - cur2 + 1 ,在本次递归中 nums[ cur1 ] 这个数据的逆序对就获取完了,让 cur1++ 

        刚好对应归并排序,由于要按照递减排序,所以 nums[ cur1 ] > nums[ cur2 ] ,就将 cur1 指向的数据放到辅助数组 helpNums 中,此时 index 数组中的数据也要对应发生改变,放到辅助数组 helpIndex 中,再让 cur1 ++,

        (2).nums[ cur1 ] <= nums[ cur2 ] ,根据递减排序的单调性,因为 cur1 指针右边的数据都小于 cur2 指针指向的数据,所以没有数据能和 nums[ cur2 ] ,构成逆序对,让 cur2 ++,

        刚好对应归并排序,由于要按照递减排序,所以 nums[ cur1 ] 《= nums[ cur2 ] ,就将 cur2 指向的数据放到辅助数组 helpNums 中,此时 index 数组中的数据也要对应发生改变,放到辅助数组 helpIndex 中,再让 cur2 ++,

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1345451.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Javaweb之Mybatis入门的详细解析

Mybatis入门 前言 在前面我们学习MySQL数据库时&#xff0c;都是利用图形化客户端工具(如&#xff1a;idea、datagrip)&#xff0c;来操作数据库的。 在客户端工具中&#xff0c;编写增删改查的SQL语句&#xff0c;发给MySQL数据库管理系统&#xff0c;由数据库管理系统执行S…

分享好用稳定快递查询api接口(对接简单)

提供实时查询和自动识别单号信息。稳定高效&#xff0c;调用简单方便&#xff0c;性价比高&#xff0c;一条链接即可对接成功。 使用数据平台该API接口需要先注册后申请此API接口。申请成功后赠送免费次数&#xff0c;可直接在线请求接口数据。 接口地址&#xff1a;https://…

迭代归并:归并排序非递归实现解析

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《数据结构&算法》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! &#x1f4cb; 前言 归并排序的思想上我们已经全部介绍完了&#xff0c;但是同时也面临和快速排序一样的问题那就是递…

『亚马逊云科技产品测评』活动征文|云服务器如何快速搭建个人博客(图文详解)

授权声明&#xff1a;本篇文章授权活动官方亚马逊云科技文章转发、改写权&#xff0c;包括不限于在 Developer Centre, 知乎&#xff0c;自媒体平台&#xff0c;第三方开发者媒体等亚马逊云科技官方渠道 文章目录 引言一、前期准备步骤1.1 准备一个亚马逊 EC2 服务器1.2 进入控…

Stimulsoft BI Designer 2024.1.2 Crack

Stimulsoft BI Designer Do you want to create reports and dashboards, but you do not need to create your application? We want to offer you a great solution – Stimulsoft Designer! All you need is to connect your data, drag it onto the template page, config…

uniapp打包Android、Ios、微信小程序

首先我们需要在我们的代码中&#xff0c;把我们所要用到的配置信息配置好&#xff0c;在检查一下我们测试的内容是否有打开&#xff08;取消注释&#xff09;&#xff0c;在检查一下我们的版本信息是否正确&#xff0c;查看一下接口ip是否是正式线 这里的配置信息一定要配置好…

二进制、位运算和掩码运算,小白鼠测试示例

1. 二进制 二进制是一种基于两个数字0和1的数制系统。它可以表示两种状态&#xff0c;即开和关。所有输入电脑的任何信息最终都要转化为二进制。目前通用的是ASCII码。最基本的单位为bit。 在计算机科学中&#xff0c;二进制是最常用的数制系统&#xff0c;因为计算机内部的所…

【MySQL】主从异步复制配置

您好&#xff0c;我是码农飞哥&#xff08;wei158556&#xff09;&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f4aa;&#x1f3fb; 1. Python基础专栏&#xff0c;基础知识一网打尽&#xff0c;9.9元买不了吃亏&#xff0c;买不了上当。 Python从入门到精…

解锁大数据世界的钥匙——Hadoop HDFS安装与使用指南

目录 1、前言 2、Hadoop HDFS简介 3、Hadoop HDFS安装与配置 4、Hadoop HDFS使用 5、结语 1、前言 大数据存储与处理是当今数据科学领域中最重要的任务之一。随着互联网的迅速发展和数据量的爆炸性增长&#xff0c;传统的数据存储和处理方式已经无法满足日益增长的需求。…

LabVIEW在大型风电机组状态监测系统开发中的应用

LabVIEW在大型风电机组状态监测系统开发中的应用 风电作为一种清洁能源&#xff0c;近年来在全球范围内得到了广泛研究和开发。特别是大型风力发电机组&#xff0c;由于其常常位于边远地区如近海、戈壁、草原等&#xff0c;面临着恶劣自然环境和复杂设备运维挑战。为了提高风电…

C语言函数篇——scanf()函数介绍

好的&#xff0c;让我们以输入/输出函数中的scanf()为例&#xff0c;来详细介绍并展示其应用案例。 scanf()函数介绍&#xff1a; scanf()函数是C语言中用于从标准输入&#xff08;通常是键盘&#xff09;读取数据的函数。它可以从用户处获取各种类型的数据&#xff0c;并将其…

11|代理(上):ReAct框架,推理与行动的协同

11&#xff5c;代理&#xff08;上&#xff09;&#xff1a;ReAct框架&#xff0c;推理与行动的协同 在之前介绍的思维链&#xff08;CoT&#xff09;中&#xff0c;我向你展示了 LLMs 执行推理轨迹的能力。在给出答案之前&#xff0c;大模型通过中间推理步骤&#xff08;尤其…

QT上位机开发(倒计时软件)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 倒计时软件是生活中经常遇到的一种场景。比如运动跑步&#xff0c;比如学校考试&#xff0c;比如论文答辩等等&#xff0c;只要有时间限制规定的地…

电影《海王2》观后感

上周看了电影《海王2》&#xff0c;整体特效和打斗还是非常不错的&#xff0c;自己在写文章的时候&#xff0c;看完电影已经一周了&#xff0c;相当于是叙事自我在描述这段经历。 &#xff08;1&#xff09;体验自我VS叙事自我 首先简单说明下“体验自我”和“叙事自我”&…

Redis经典五大类型源码及底层实现(二)

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱吃芝士的土豆倪&#xff0c;24届校招生Java选手&#xff0c;很高兴认识大家&#x1f4d5;系列专栏&#xff1a;Spring源码、JUC源码、Kafka原理、分布式技术原理、数据库技术&#x1f525;如果感觉博主的文章还不错的…

Cuk、Zeta和Sepic开关电源拓扑结构

Cuk、Zeta和Sepic变换器,三种拓扑结构大致类似。不同点在于电感和二极管&#xff0c;MOS管的位置关系的变化。 Cuk电源是一种非隔离的直流电源转换器&#xff0c;其基本结构包括输入滤波电容、开关管、输入电感、输出电感和输出电容等元件。Cuk电路可以看作是Boost和Buck电路的…

【CFP-专栏2】计算机类SCI优质期刊汇总(含IEEE/Top)

一、计算机区块链类SCI-IEEE 【期刊概况】IF:4.0-5.0, JCR2区&#xff0c;中科院2区&#xff1b; 【大类学科】计算机科学&#xff1b; 【检索情况】SCI在检&#xff1b; 【录用周期】3-5个月左右录用&#xff1b; 【截稿时间】12.31截稿&#xff1b; 【接收领域】区块链…

基于轻量级GhostNet模型开发构建生活场景下生活垃圾图像识别系统

轻量级识别模型在我们前面的博文中已经有过很多实践了&#xff0c;感兴趣的话可以自行移步阅读&#xff1a; 《移动端轻量级模型开发谁更胜一筹&#xff0c;efficientnet、mobilenetv2、mobilenetv3、ghostnet、mnasnet、shufflenetv2驾驶危险行为识别模型对比开发测试》 《基…

Dragonfly-SM X9H核心板 SM6700Q PMIC供电配置烧录介绍

一、概述 核心板采用 1 片芯迈 SM6700Q PMIC 芯片搭配 3 片 MPQ8861 DCDC 电源芯片和 2 片安森美 LDO&#xff0c;型号分别 NCV8164ASN180T1G 和 NCV8130BMX080TCG 为系统供电。 二、核心板供电框图 系统供电主要是 MCU 的 RTC 域、安全域、应用域的供电&#xff0c;其中 RTC 域…

数据库中的锁

锁 锁冲突是针对事务的&#xff0c;另一个事务能不能申请上锁&#xff0c;是和其他事务竞争。 一个事务内部可以加很多锁&#xff0c;互相并不会冲突。 级联回滚调度 多个事务有依赖关系&#xff0c;如果一个事务回滚&#xff0c;那么所有事务也需要回滚。 冲突的数据加锁安…