JS leetcode 有多少小于当前数字的数字 解题分析,你应该了解的桶排序
壹 ❀ 引
刷题其实一直没断,只是这两天懒得整理题目...那么今天来记录一道前天做的题,题目本身不难,不过拓展起来还是有些东西可以讲,题目来自leetcode有多少小于当前数字的数字,题目描述如下:
给你一个数组 nums,对于其中每个元素 nums[i],请你统计数组中比它小的所有数字的数目。
换而言之,对于每个 nums[i] 你必须计算出有效的 j 的数量,其中 j 满足 j != i 且 nums[j] < nums[i] 。
以数组形式返回答案。
示例 1:
输入:nums = [8,1,2,2,3]
输出:[4,0,1,1,3]
解释:
对于 nums[0]=8 存在四个比它小的数字:(1,2,2 和 3)。
对于 nums[1]=1 不存在比它小的数字。
对于 nums[2]=2 存在一个比它小的数字:(1)。
对于 nums[3]=2 存在一个比它小的数字:(1)。
对于 nums[4]=3 存在三个比它小的数字:(1,2 和 2)。
示例 2:
输入:nums = [6,5,4,8]
输出:[2,1,0,3]
示例 3:
输入:nums = [7,7,7,7]
输出:[0,0,0,0]
提示:
2 <= nums.length <= 500
0 <= nums[i] <= 100
要求很简单,对于数组中每个元素,统计比它要小的元素的个数,我们来尝试解答它。
贰 ❀ 解题思路
那么我首先想到的比较暴力的做法自然是使用双循环嵌套了,设置一个用于统计个数的变量,外层循环每次取一个元素出来,依次跟内层循环的每个元素对比,只要满足条件,让计数变量自增。
说干就干,实现是这样:
贰 ❀ 壹 使用双循环
/**
* @param {number[]} nums
* @return {number[]}
*/
var smallerNumbersThanCurrent = function (nums) {
let ans = [],
len = nums.length;
// 每次取一位出来作为参照
for (let i = 0; i < len; i++) {
// 计数变量
let sum = 0;
for (let j = 0; j < len; j++) {
if (nums[i] > nums[j]) {
++sum;
};
};
ans.push(sum);
};
return ans;
};
贰 ❀ 贰 排序加indexOf
在实现后,我脑中有了一个想法,能不能把数组排个序,比如[8,1,2,2,3]
排序后是[1,2,2,3,8]
,这样我能知道每个元素前有几个元素,不就是我们想要的答案了。
当然这只是一个模糊的想法,有两个问题需要解决,一是我怎么知道有几个元素比当前元素小,特别是当元素相同的时候,比如[7,7,7,8]
。
比第一个7要小的数为0个,第二个7也是0个,第三个7还是0个,而第四个8是三个,0,0,0,3。这不是就在用indexOf
获取索引吗,管你元素相不相同,相同获取第一个数字的索引就行了。
第二个问题是,一旦数组被我们排序,虽然能得到对应的个数,但这个个数的顺序也被打乱了,怎么还原呢?别忘了我们还可以拷贝一份数组。那么到这里我们来看看第二种实现:
/**
* @param {number[]} nums
* @return {number[]}
*/
var smallerNumbersThanCurrent = function (nums) {
let ans = [],
// 拷贝一份数组,用于维持ans查询结果顺序
nums_ = JSON.parse(JSON.stringify(nums));
//将nums排序好
nums = nums.sort((a, b) => a - b);
nums_.forEach(item => {
ans.push(nums.indexOf(item));
});
return ans;
};
在评论区,我看到有用户在拷贝数组时使用的是[].concat(nums)
,比如:
let arr = [1,2,3];
let arr_ = [].concat(arr);
arr_;//[1,2,3]
那么有个问题,concat
是浅拷贝还是深拷贝?如果我们修改上面例子中的arr,你会发现互不影响:
arr.push(4);
arr_;//[1,2,3]
这能说明concat
是深拷贝吗?其实在MDN中关于concat
描述很清楚,此方法会依次拷贝目标数组中每个元素,而对于元素类型不同做不同决策。如果元素是简单类型,则直接复制元素,但如果元素是对象类型,而是拷贝对象的引用,所以我们可以来看一个对象数组的类型,比如:
let arr = [1,[2,3],4];
let arr_ = [].concat(arr);
arr[1].push(1);
arr_;//[1,[2,3,1],4]
那么关于此方案说到这了。当然除了以上两种方案外,还有一种是官方提到的计数排序+求前缀和的做法。当时相比官方所说的计数排序,我觉得更像桶排序,由于这两种排序十分类似,我暂时未能分清他们,所以这里暂且叫桶排序吧。
我知道第一次听说桶排序的同学一定会很陌生,没事,我们先来简单介绍它,以[3,2,2,1,4]
为例。
由于数组中最大元素是4,所以我们建立五个用于装元素的桶,桶默认值都是0,那为啥是五个?因为数组默认索引是从0开始的,0-1-2-3-4,这不是5个桶吗?

现在我们开始遍历[3,2,2,1,4]
,将数字丢到对应索引的桶中,每丢一个进去让桶的初始值自增1,像这样:

现在我们得到了一个装满数字的桶,因为他也是一个数组,遍历桶,比如桶1的数量为1,输出1个1,桶2数量是2,所以输出2个2,以此类推:

你看,我们不就得到了一个已经排序好的数组了,让我们尝试实现它(注意,这并非官方的桶排序,我只是按照这个思路去实现它)。
// 非正式桶排序
function bucketSort(nums) {
// 找出最大元素
let max = Math.max(...nums),
// 创建桶
bucket = new Array(max + 1).fill(0),
ans = [];
// 统计对应桶位元素数量
nums.forEach(ele => {
bucket[ele]++;
});
for (var i = 0; i < bucket.length; i++) {
while (bucket[i] > 0) {
ans.push(i);
bucket[i]--;
};
};
return ans;
};
bucketSort([3, 2, 2, 1]); //[1,2,2,3]
OK,知道了这个怎么能用于解题呢?仔细想想,当我们桶排序后,对应每个桶的数量,不就是我们希望要的数字吗,比如上方图解中:

比桶1小的元素位0个,也就是bucket[1-1]
个。
比桶2小的元素有1个,也就是bucket[1-1]+bucket[2-1]
个。
比桶3小的元素有3个,也就是bucket[3-1]+bucket[3-2]+bucket[3-3]
个。
比桶4小的元素有4个,也就是bucket[4-1]+bucket[4-2]+bucket[4-3]+bucket[4-4]
个。
当然有个特例,因为桶0是一个,不会有比它小的元素了,所以比它小的一定是0个。
这也是我们在前面说官方用计数排序加求前缀和做法,为何要求前缀和的原因。我知道这有点绕,静下心仔细想想,这只是用了桶排序统计了元素的个数,元素成了桶数组的下标,顺带求了数量的和。
现在让我们实现它,像这样:
/**
* @param {number[]} nums
* @return {number[]}
*/
var smallerNumbersThanCurrent = function(nums) {
let ans = [],
max = Math.max(...nums),
len = nums.length;
// 由于i范围是0 <= nums[i] <= 100,因此创建101个桶
let bucket = new Array(max + 1).fill(0);
// 将元素装到对应的桶
for (let i = 0; i < len; i++) {
// 数字每出现一次,桶的统计数量就加1
bucket[nums[i]]++;
};
// 求n项出现的次数和,考虑越界情况,此处i从1开始
for (let i = 1; i <= 100; i++) {
bucket[i] += bucket[i - 1]
};
// nums中元素对应了桶的索引,索引非0取前一位,索引为0说明没有比它小的,直接赋予0
for (let i = 0; i < len; i++) {
nums[i] ? ans[i] = bucket[nums[i] - 1] : ans[i] = 0
};
return ans;
};
那么关于本题就分析到这里了。
JS leetcode 有多少小于当前数字的数字 解题分析,你应该了解的桶排序的更多相关文章
- LeetCode:移除K位数字【402】
LeetCode:移除K位数字[402] 题目描述 给定一个以字符串表示的非负整数 num,移除这个数中的 k 位数字,使得剩下的数字最小. 注意: num 的长度小于 10002 且 ≥ k. nu ...
- js正则表达式,判断字符串是否以数字组结尾,并取出结尾的数字
js正则表达式,判断字符串是否以数字组结尾,并取出结尾的数字 <!DOCTYPE html> <html> <head> <meta charset=&quo ...
- js 关于字符串转数字及数字保留位数的控制
1.parseInt()和parseFloat()两个转换函数,将字符串转换成相应的数字. 1.parseInt() parseInt进行转换时,将字符串转成相应的整数.浮点数以后的数字都不要了. p ...
- HDU 4352 区间的有多少个数字满足数字的每一位上的数组成的最长递增子序列为K(数位DP+LIS)
题目:区间的有多少个数字满足数字的每一位上的数组成的最长递增子序列为K 思路:用dp[i][state][j]表示到第i位状态为state,最长上升序列的长度为k的方案数.那么只要模拟nlogn写法的 ...
- js正则表达式限制文本框只能输入数字,小数点,英文字母
1.文本框只能输入数字代码(小数点也不能输入)<input onkeyup="this.value=this.value.replace(/\D/g,'')" onafter ...
- Leetcode之二分法专题-374. 猜数字大小(374. Guess Number Higher or Lower)
Leetcode之二分法专题-374. 猜数字大小(374. Guess Number Higher or Lower) 我们正在玩一个猜数字游戏. 游戏规则如下:我从 1 到 n 选择一个数字. 你 ...
- js运算【按位非】~ (index = ~~this.userIndex)(~~ 双破折号 如果是数字返回数字,如果不是数字 返回0)
index = ~~this.userIndex ~~ 双破折号 如果是数字返回数字,如果不是数字 返回0 这个运算符有点意思:按位非[~] 先来几个例子: ~undefined: -1 ~false ...
- Input 只能输入数字,数字和字母等的正则表达式
JS只能输入数字,数字和字母等的正则表达式 1.文本框只能输入数字代码(小数点也不能输入) <input onkeyup="this.value=this.value.replace( ...
- cell1这个字符串如何截取掉前边的cell剩下后边的数字 后边数字长度不固定
cell1这个字符串如何截取掉前边的cell剩下后边的数字 后边数字长度不固定'cell1'.replace(/cell/,'')string.substr(4)string.slice(4)
- C# 之 Excel 导入一列中既有汉字又有数字:数字可以正常导入,汉字导入为空
今天在做一个Excel导入功能,一切开发就绪,数据可以成功导入.导入后检查数据库发现有一列既有汉字又有数字,数字正常导入,汉字为空.但是前面同样既有汉字又有数字的列可以导入成功. 查看excel 源文 ...
随机推荐
- python中BeautifulSoup库使用小结
转载请注明出处: BeautifulSoup是一个用于解析HTML和XML文档的Python库,它提供了一些简单但强大的API,让你可以从文档中提取数据.以下是一些BeautifulSoup的主要特性 ...
- idea相关配置及插件安装
对idea相关的配置及好用的插件进行总结下. 一.idea 破解码及配置:https://www.jb51.net/softs/672190.html 二.idea插件: 1.findBugs-ide ...
- 【Hash】字符串哈希
Hash 的核心思想在于,将输入映射到一个值域较小.可以方便比较的范围,典型的用法就是将资源紧张的设备中的不定长字符串转化为定长整数,以达到节省空间的目的 如:printf("This is ...
- [转帖]在 Linux 上以 All-in-One 模式安装 KubeSphere
https://www.kubesphere.io/zh/docs/v3.4/quick-start/all-in-one-on-linux/ 对于刚接触 KubeSphere 并想快速上手该容器平台 ...
- OpenGauss 单机版安装
OpenGauss 单机版安装 银河麒麟的前置事项 yum -y install libaio-devel flex bison ncurses-devel glibc-devel patch rea ...
- [转帖]麒麟v10上部署TiDBv5.1.2生产环境的最佳实践
https://tidb.net/book/tidb-monthly/2022/2022-07/usercase/tidb-v5-1-2 前言 笔者最近在一个银行项目中做 PoC 测试,由于客户选择 ...
- [转帖]Codis作者黄东旭:细说分布式Redis架构设计和那些踩过的坑
https://dbaplus.cn/news-141-270-1.html Codis是一个分布式Redis解决方案,与官方的纯P2P模式不同,Codis采用的是Proxy-based的方案.今天我 ...
- [转帖]【JVM】类加载机制
什么是类的加载 将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构.类的加载的最终产 ...
- redis 6源码解析之 dict
edis源码的dict.c主要实现了基于hash表的操作,如增删改查,对哈希表大小的扩容和缩容,以及对哈希表的rehash和增量rehash等.在源码的dictScan函数中,非常巧妙精美地实现了对哈 ...
- vue3中retive的错误用法导致数据不跟新
retive的错误用法 <template> <div> 司藤的信息==>{{ objInfo }} <button @click="handerHttp ...