[LeetCode/力扣][Java] 0315. 计算右侧小于当前元素的个数(Count of Smaller Numbers After Self)

news2024/10/6 22:20:09

题目描述:

给你一个整数数组 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 个更小的元素

思路:一开始的思路是从后往前遍历,然后当前元素i的后边元素保持降序sort(i+1, n)。但是最后几个测试用例会超时。
代码1

class Solution {
    public  List<Integer> countSmaller(int[] nums) {
        int n = nums.length;
        int[] counts = new int[n];
        int min = nums[n-1];
        counts[n-1] = 0;
        for (int i = n-2; i >= 0; i--) {
            int j = i+1;
            int cur = nums[i];
            if (n > 49999 && cur < min) { //几个特殊用例可以打补丁防止超时但仍然不能ac
                min = cur;
                counts[i] = 0;
                continue;
            }
            while (j < n && cur <= nums[j]) {
                nums[j-1] = nums[j];
                j++; 
            }
            nums[j-1] = cur;
            counts[i] = n-j;
            //Arrays.sort(nums, cmp, i, n);
        }
        List<Integer> res = new ArrayList<Integer>();
        for (int i = 0; i < n; i++) {
            res.add(counts[i]);
        }
        return res;
    }
}

思路2

上边的代码可以过掉64/66个用例(虽然偷懒了)。但其他的解法确实想不到了(后来看了题解可以优化成二分插入,时间复杂度由O(n^2)降到O(nlogn)列在代码2)。于是评论区看题解发现树状数组,归并排序,二分查找等都是没用过比较陌生的知识点,于是将笔记汇总于此。

代码2(有序数组+二分查找)

class Solution {
    public  List<Integer> countSmaller(int[] nums) {
        int n = nums.length;
        List<Integer> list = new ArrayList<Integer>();  //用于存储右侧部分值,执行二分查找+插入
        int[] counts = new int[n];      //记录结果
        counts[n-1] = 0;
        list.add(nums[n-1]);
        for (int i = n-2; i >= 0; i--) {
            int cur = nums[i];
            int left = 0;
            int right = list.size()-1;
   			//二分查找
            while (left <= right) {
                int mid = (left + right) / 2;
                if (cur > list.get(mid)) left = mid + 1;
                else right = mid - 1; 
            }
            list.add(left, cur);
            counts[i] = left;
        }
        List<Integer> res = new ArrayList<Integer>();
        for (int i = 0; i < n; i++) {
            res.add(counts[i]);
        }
        return res;
    }
}

树状数组就是利用到了十进制与二进制之间的关系,是一个非常巧妙的用于求解前缀和的数据结构。树状数组的原理可以在b站上搜到,很多up用动画讲解的非常到位。这里给出一张图简单解释一下。
树状数组
  对于一个有序数组,当我们求前缀和的时候,不必要依次累加,而是用若干区间累加即可。例如 sum(1,5)=sum(1,4)+sum(4,5)。那这个向下递减的过程就是靠二进制实现的,(5)2=101,用它减去最右侧的1及其后的0之后变成(4)2=100。递减过程中需保证其大于0(因为数组是从1开始的)。这个求最右侧的1及其后的0是通过lowbit函数实现的,具体原理涉及计租中的补码和反码,这里不详细展开了(我也不太明白)。通过树状数组,我们可以在O(logn)的时间内求出任意位置的前缀和。
  第二个问题就是树状数组的维护,在普通数组中,更新一个数值只需更改nums[i]的数值即可。但是在树状数组中,一个位置可能被多个区间包含,比如上图中的nums[2]对应四个区间tree[2],tree[4],tree[8],tree[16]。因此更新nums[2]的数值需要更新四个树状数组的数值。这其实就是反向加lowbit即可。(因为tree[2]就是通过减lowbit得到的嘛)

代码3(树状数组)

class Solution {
    static int MAXN = 20001;
    static int addNum = 10001;	//将所有负数转变为正数
    static int[] tree = new int[MAXN]; //tree中存储的是出现频次
    public void update(int idx, int val) {
        for (int i = idx; i < MAXN; i += lowbit(i)) {
            tree[i] += val;
        }
    }
    public int query(int n) {
        int res = 0;
        for (int i = n; i > 0; i -= lowbit(i)) {
            res += tree[i];
        }
        return res;
    } 
    public int lowbit(int n) {return n & (-n);}

    public  List<Integer> countSmaller(int[] nums) {
        int len = nums.length;
        List<Integer> res = new ArrayList<Integer>();
        //从前往后遍历,第一个数除去自身之后,所有比它小的数的个数即是它的结果。
        //每查询完自己就在树状数组中减掉自己的频次,
        //这样每次都是在整个树状数组范围内查找当前值的前一个位置的前缀和.
        for (int i = 0; i < len; i++) {
            update(nums[i]+addNum, 1);
        } 
        for (int i = 0; i < len; i++) {
            update(nums[i]+addNum, -1);
            res.add(query(nums[i]+addNum-1));	//查询的是前一个位置的前缀和。
        }
        return res;
    }
}

最后关于归并排序和二叉搜索树的解法就不重复造轮子了,可参考彼得·攀的小站。

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

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

相关文章

CSS3------什么是css

什么是CSS 层叠样式表Cascading Style Sheets&#xff0c;缩写为CSS&#xff0c;是一种样式表语言&#xff0c;用来描述HTML或XML&#xff08;包括如SVG、MathML、XHTML 之类的XML 分支语言&#xff09;文档的呈现。 CSS描述了在屏幕、纸质、音频等其它媒体上的元素应该如何被…

uniapp之路由中携带参数跳转

目录 前言 一 路由跳转方式 1. 直接在 template中定义 2.直接在methods中定义 二 携带参数 1.在template中定义 2.在methods里定义 3. 拼接 前言 在我们写 uniapp 小程序时&#xff0c;时常遇到的就是路由携带参数进行跳转&#xff0c;这项功能似乎已成家常便饭一样&am…

(八)笔记.net core学习之特性Attribute声明、使用、验证

1.特性Attribute 特性&#xff1a;是用于在运行时传递程序中各种元素&#xff08;比如类、方法、结构、枚举、组件等&#xff09;的行为信息的声明性标签。您可以通过使用特性向程序添加声明性信息。一个声明性标签是通过放置在它所应用的元素前面的方括号&#xff08;[ ]&…

缺陷修改实践——replace函数的运用|思考?

目录介绍问题出现问题分析解决方法优化实现总结介绍 大家好&#xff0c;我是清风。今天给大家分享一个项目中遇到问题解决问题的案例&#xff0c;编程其实就是一个思考的过程&#xff0c;缺少思考就没有灵魂&#xff0c;遇到问题先静下心去思考&#xff0c;想到方法后再去实践。…

HTML小游戏11 —— 横版恐龙大冒险游戏(附完整源码)

&#x1f482; 网站推荐:【神级源码资源网】【摸鱼小游戏】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 想寻找共同学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】&#x1f4ac; 免费且实用的计…

U++ 插件学习笔记

Type&#xff1a; Editor&#xff1a;插件只能在编辑器有效 Runtime&#xff1a;代表打包出来也会有插件功能 Developer&#xff1a;开发者模式可以使用插件功能 Program&#xff1a;独立的小程序 ServerOnly&#xff1a;服务端可以使用插件功能 ClientOnly&#xff1a;客户…

三西格玛和六西格玛区别是什么?优思学院用一幅图告诉你

3西格玛和6西格玛的质量水平相距甚远&#xff0c;这个视频中用了三个实例说明了两者在不同行业上的具体绩效表现。优思学院认为&#xff0c;企业只有追求完美&#xff0c;不断改进流程&#xff0c;才能不断超越现状&#xff0c;才可以取得更大的业绩。 西格玛水平代表不同的过程…

朋友电脑密码忘了,我当场拔了她的电源,结果。。。

「作者主页」&#xff1a;士别三日wyx 「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 Windows密码忘了怎么办&#xff1f;一、5次shift键弹出粘滞键二、异常断电触发系统的自动修复三、未登录修改系统文件四、登录界面5次shift键…

【云原生 | 45】Docker搭建Registry私有仓库之配置Registry详解

&#x1f341;博主简介&#xff1a; &#x1f3c5;云计算领域优质创作者 &#x1f3c5;2022年CSDN新星计划python赛道第一名 &#x1f3c5;2022年CSDN原力计划优质作者 &#x1f3c5;阿里云ACE认证高级工程师 &#x1f3c5;阿里云开发者社区专…

连接SQL Server 数据库

目录 一、启动 SQL Server Management Studio 1. 点击 SQL Server Management Studio 菜单进入 2. 选择服务器和身份验证方式 3. 点击连接进入数据库 二、新建数据库 1.数据库的概念 2. 看看当前有哪些数据库 3. 新建数据库 三、新建查询 1. 选中 test 数据库…

编码踩坑——记一次fastjson引发的空指针问题、引用标识$ref

本篇介绍在使用fastjson过程中遇到的一个问题&#xff0c;从而引申出使用fastjson时的注意事项——&#xff08;1&#xff09;尽量避免在实体中定义 get 开头的方法&#xff1b;&#xff08;2&#xff09;避免较深的实体字段层级&#xff1b;&#xff08;3&#xff09;避免实体…

企业信息化改革怎么做?

前几天遇到一位朋友提问&#xff0c;他说他们公司目前需要进行信息化改革&#xff0c;问我有哪些好用的信息化管理系统推荐&#xff1f;并附上了几点要求&#xff1a; 扩展性强&#xff08;公司管理结构变化很快&#xff0c;套装软件扩展升级太麻烦&#xff0c;并不适合&#…

怎样建立自己网站?【建立网站】

怎样建立自己网站&#xff1f;以前建立网站大部分情况下都是需要懂编程&#xff0c;又或者是懂一点代码然后去找源码模板做二次开发。不过随着技术的发展&#xff0c;建立自己网站的方式也在变多&#xff0c;例如目前比较流行的自助搭建网站。那么怎样建立自己网站呢&#xff1…

全卷积神经网络图像去噪研究-含Matlab代码

⭕⭕ 目 录 ⭕⭕✳️ 一、引言✳️ 二、网络结构✳️ 三、实验结果✳️ 3.1 数据集与网络训练✳️ 3.2 卷积神经网络去噪实验✳️ 3.3 基于BM3D的对比实验✳️ 四、参考文献✳️ 五、Matlab代码获取✳️ 一、引言 图像去噪在底层视觉中的重要性可以从多方面体现出来。首先&…

批量导入Npm包依赖到Nexus私服(批量上传脚本)

背景 批量导入是在以下几点情况下产生的需求&#xff1a; 已有Nexus系统&#xff0c;在测试构建环境中Nexus系统为离线环境不能配置外网代理自动下载项目代码工程所在工作电脑有条件联网现需要将项目代码在测试构建环境中连接Nexus私服进行编译构建 基于上面几点情况&#x…

HTML+CSS美食静态网站设计【海鲜网站】web结课作业的源码 web网页设计实例作业

&#x1f380; 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

Spring Security(3)

您好&#xff0c;我是湘王&#xff0c;这是我的CSDN博客&#xff0c;欢迎您来&#xff0c;欢迎您再来&#xff5e; 前面运行写好的代码之所以没有任何显示&#xff0c;是因为还没有对Spring Security进行配置&#xff0c;当然啥也不显示了。这就好比你坐在车上&#xff0c;却不…

智慧灾备解决方案-最新全套文件

智慧灾备解决方案-最新全套文件一、建设背景二、建设思路三、建设方案四、获取 - 智慧灾备全套最新解决方案合集一、建设背景 随着“云物移大智”各种技术的飞速发展&#xff0c;信息系统种类越来越多&#xff0c;数据保护也备受关注&#xff0c;传统数据保护往往仅覆盖数据库…

(CVPR 2019) 3D-SIS: 3D Semantic Instance Segmentation of RGB-D Scans

图 1&#xff1a;3D-SIS对RGB-D扫描数据执行3D实例分割&#xff0c;学习将2D RGB输入特征与3D扫描几何特征联合融合。结合能够在测试时对全3D扫描进行推理的全卷积方法&#xff0c;我们实现了对目标边界框、类标签和实例掩码的准确推理。 Abstract 我们介绍了3D-SIS&#xff0…

编译原理实验--实验三 预测分析法判断算术表达式的正确性--Python实现

目录 一、实验目的和要求 二、实验内容 三、实验环境 四、实验步骤 1、语法分析所依据的文法&#xff1b; 2、给出消除左递归及提取左公因子的LL(1)文法&#xff1b; 3、预测分析表 4、关键代码 五、实验结果与分析 一、实验目的和要求 理解自顶向下语法分析方法&#…