壹 ❀ 引

刷题其实一直没断,只是这两天懒得整理题目...那么今天来记录一道前天做的题,题目本身不难,不过拓展起来还是有些东西可以讲,题目来自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 有多少小于当前数字的数字 解题分析,你应该了解的桶排序的更多相关文章

  1. LeetCode:移除K位数字【402】

    LeetCode:移除K位数字[402] 题目描述 给定一个以字符串表示的非负整数 num,移除这个数中的 k 位数字,使得剩下的数字最小. 注意: num 的长度小于 10002 且 ≥ k. nu ...

  2. js正则表达式,判断字符串是否以数字组结尾,并取出结尾的数字

    js正则表达式,判断字符串是否以数字组结尾,并取出结尾的数字 <!DOCTYPE html> <html> <head> <meta charset=&quo ...

  3. js 关于字符串转数字及数字保留位数的控制

    1.parseInt()和parseFloat()两个转换函数,将字符串转换成相应的数字. 1.parseInt() parseInt进行转换时,将字符串转成相应的整数.浮点数以后的数字都不要了. p ...

  4. HDU 4352 区间的有多少个数字满足数字的每一位上的数组成的最长递增子序列为K(数位DP+LIS)

    题目:区间的有多少个数字满足数字的每一位上的数组成的最长递增子序列为K 思路:用dp[i][state][j]表示到第i位状态为state,最长上升序列的长度为k的方案数.那么只要模拟nlogn写法的 ...

  5. js正则表达式限制文本框只能输入数字,小数点,英文字母

    1.文本框只能输入数字代码(小数点也不能输入)<input onkeyup="this.value=this.value.replace(/\D/g,'')" onafter ...

  6. Leetcode之二分法专题-374. 猜数字大小(374. Guess Number Higher or Lower)

    Leetcode之二分法专题-374. 猜数字大小(374. Guess Number Higher or Lower) 我们正在玩一个猜数字游戏. 游戏规则如下:我从 1 到 n 选择一个数字. 你 ...

  7. js运算【按位非】~ (index = ~~this.userIndex)(~~ 双破折号 如果是数字返回数字,如果不是数字 返回0)

    index = ~~this.userIndex ~~ 双破折号 如果是数字返回数字,如果不是数字 返回0 这个运算符有点意思:按位非[~] 先来几个例子: ~undefined: -1 ~false ...

  8. Input 只能输入数字,数字和字母等的正则表达式

    JS只能输入数字,数字和字母等的正则表达式 1.文本框只能输入数字代码(小数点也不能输入) <input onkeyup="this.value=this.value.replace( ...

  9. cell1这个字符串如何截取掉前边的cell剩下后边的数字 后边数字长度不固定

    cell1这个字符串如何截取掉前边的cell剩下后边的数字  后边数字长度不固定'cell1'.replace(/cell/,'')string.substr(4)string.slice(4)

  10. C# 之 Excel 导入一列中既有汉字又有数字:数字可以正常导入,汉字导入为空

    今天在做一个Excel导入功能,一切开发就绪,数据可以成功导入.导入后检查数据库发现有一列既有汉字又有数字,数字正常导入,汉字为空.但是前面同样既有汉字又有数字的列可以导入成功. 查看excel 源文 ...

随机推荐

  1. 以太网链路连接 和 ISIS/OSPF等路由协议关系

    转载请注明出处: 以太网链路连接和ISIS/OSPF协议之间存在关联和区别 关联: 以太网链路连接是指通过以太网物理媒介(如电缆)将网络设备进行连接,使它们可以交换数据. ISIS(Intermedi ...

  2. Ribbon 负载均衡源码解读

    转载请注明出处: 1.什么是Ribbon 是 Netflix 发布的一个负载均衡器,有助于控制 HTTP 和 TCP客户端行为.在 SpringCloud 中, nacos一般配合Ribbon进行使用 ...

  3. 【架构师视角系列】Apollo配置中心之Client端(二)

    原创文章,转载请标注.https://www.cnblogs.com/boycelee/p/17978027 目录 声明 配置中心系列文章 一.客户端架构 1.Config Service职责 (1) ...

  4. [转帖]s3fs把 s3-like 对象存储挂载到本地

    s3fs把 s3-like 对象存储挂载到本地 s3fs把 s3-like 对象存储挂载到本地 s3fs-fuse 是一个采用 c++ 开发的开源应用,它的作用是可以将 AWS S3 以及兼容 S3 ...

  5. [转帖]Day63_Kafka(一)

    第一讲 Kafka基础操作 课程大纲 课程内容 学习效果 掌握目标 Kafka简介 消息队列 掌握 Kafka简介 Kafka分布式环境 Kafka操作 Kafka shell 掌握 Kafka ap ...

  6. 多个物理磁盘挂载到同一目录的方法 (lvm 软raid)

    多个物理磁盘挂载到同一目录的方法 (lvm 软raid) 背景 公司里面的一台申威3231的机器 因为这个机器的raid卡没有操作界面. 所以只能够通过命令行方式创建raid 自己这一块比较菜, 想着 ...

  7. [转帖]wmic命令介绍

    https://www.jianshu.com/p/3e1a5a8fa23b How to Get Your System Serial Number PS C:\windows\system32&g ...

  8. Redis scan等命令的学习与研究

    Redis scan等命令的学习与研究 摘要 背景跟前几天说的一个问题类似. 为了验证自己的设想, 所以晚上继续写脚本进行了一轮次的验证. 不过上次讨论时,打击好像都没听懂我说的 所以这次准备从基础开 ...

  9. [转帖]2.6 The jcmd Utility

    https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/tooldescr006.html#BABEJDGE The j ...

  10. C#开源免费的开发效率提升利器:DevToys开发人员的瑞士军刀!

    前言 今天分享一款基于C#开源(MIT License开源协议).免费.离线.功能齐全的Windows开发者工具箱,号称开发人员的瑞士军刀,可以帮助开发者完成日常工作开发中常用功能:DevToys. ...