壹 ❀ 引

整理下今天做的算法题,题目难度不高,但在优化角度也是费了一些功夫。题目来自219. 存在重复元素 II,问题描述如下:

给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,使得 nums [i] = nums [j],并且 i 和 j 的差的 绝对值 至多为 k。

示例 1:

输入: nums = [1,2,3,1], k = 3
输出: true

示例 2:

输入: nums = [1,0,1,1], k = 1
输出: true

示例 3:

输入: nums = [1,2,3,1,2,3], k = 2
输出: false

题目意思其实很简单,看一个数组中是否有两个元素相等,且后者索引减去前者的差集,要小于等于数值k。注意不是等于k,我第一次提交就是看错了直接给挂了,接下来我们来说说怎么做。

贰 ❀ 暴力解法

我首先想到的自然是for循环遍历嵌套,用两个索引分别表示数组中一前一后的元素,如果两个元素相等,且后者索引减去前者索引的值<=k,则返回true即可:

/**
* @param {number[]} nums
* @param {number} k
* @return {boolean}
*/
var containsNearbyDuplicate = function (nums, k) {
let ans = false
for (let i = 0; i < nums.length; i++) {
// 注意这里j从i+1开始
for (let j = i + 1; j < nums.length; j++) {
// 满足两数相等,且索引差不大于k即可
if (nums[i] === nums[j] && j - i <= k) {
return true;
};
};
};
return ans;
};

叁 ❀ 震惊的负向优化

我在上篇文章中提到,如果能将时间复杂度从O(n²)降到O(n),那将是大大的优化,尽管上述代码遍历也并未达到n²,但我们还是可以试试。

在阅读了官方推荐的哈希表做法后,我实现了如下代码:

/**
* @param {number[]} nums
* @param {number} k
* @return {boolean}
*/
var containsNearbyDuplicate = function (nums, k) {
// 我们始终维护一个大小为k的哈希表
let hash = [];
for (let i = 0; i < nums.length; i++) {
// 判断有没有当前元素,开始为空肯定没有
if (hash.includes(nums[i])) {
return true;
};
// 将当前元素加入哈希表中
hash.push(nums[i]);
// 前面说了,哈希表大小始终为k,超过了,我们就删除最旧的数据
if (hash.length > k) {
hash.shift();
};
};
return false;
};

实现不难理解,我们始终维护一个大小为k的哈希表,并依次将元素加入表内,由于一开始为空,自然是加入第一个。从第二个开始判断有没有,如果有自然返回true。反之没有继续加入表内。但需要注意的是,我们的表的大小是k,一旦超过我们就得删除掉最旧的数据。

思路清晰,然后我提交了代码,一看执行时间,我人都傻了,出于怀疑我又点了一次提交(2000ms与1780ms)。

从表面上看,暴力解法用了两次循环嵌套,而哈希表做法只用了一次。其实从内部实现来看,循环嵌套做的事情要简单的多。

我们知道数组是呈线性排列的一种数据结构,当我们通过索引直接访问某条数据时,它的时间复杂度为O(1),而做查找操作就不同了,由于没有提供标识,我们只能通过线性查找一个接一个进行对比,看看当前是不是我们想要的。看看hash.includes(nums[i])这行代码,你是否意识到了什么呢?

除此之外,我们在下方代码还做了shift操作,也就是数组删除,由于数组是连续的,出现空缺就得填补,像这样:

所以整体上来看,双循环嵌套执行次数看着虽然多,但站在时间复杂度角度来说,每一次操作单元耗时微乎其微,都是根据索引直接找到对应元素对比。而后一种实现每次遍历做的事情就非常耗时了。

肆 ❀ 使用ES6 set结构

奇妙的是,同样的思路,我们将数组换成set结构,速度就快了很多,只用了88ms,这里引用灵魂画手的实现:

/**
* @param {number[]} nums
* @param {number} k
* @return {boolean}
*/
var containsNearbyDuplicate = function(nums, k) {
// 创建哈希表
const set = new Set();
for(let i = 0; i < nums.length; i++) {
// 判断有没有
if(set.has(nums[i])) {
return true;
};
set.add(nums[i]);
if(set.size > k) {
// 删除最旧数据
set.delete(nums[i - k]);
};
};
return false;
};

可以看到只是单纯换了数据结构,执行用时质的提升,这里我不禁对于数据结构差异产生了兴趣,在知乎javascript 里的Set.has和Array.includes谁的效率更高?提问中,有用户做过测试,在大量数据下,set要远高于数组。但本质原因我一时无法考证了,只能在心里埋下一枚问题种子,待日后算法与数据结构的不算学习,希望能给自己一个答案。

那么到这里,本文正式结束。

JS leetcode 存在重复元素 II 题解分析,记一次震惊的负向优化的更多相关文章

  1. 力扣(LeetCode)删除排序链表中的重复元素II 个人题解

    给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字. 思路和上一题类似(参考 力扣(LeetCode)删除排序链表中的重复元素 个人题解)) 只不过这里需要用到一个前 ...

  2. LeetCode 82,考察你的基本功,在有序链表中删除重复元素II

    本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是LeetCode专题的第51篇文章,我们来看LeetCode第82题,删除有序链表中的重复元素II(Remove Duplicates ...

  3. LeetCode 82. 删除排序链表中的重复元素 II(Remove Duplicates from Sorted List II)

    82. 删除排序链表中的重复元素 II 82. Remove Duplicates from Sorted List II 题目描述 给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中没有 ...

  4. Java实现 LeetCode 219 存在重复元素 II(二)

    219. 存在重复元素 II 给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,使得 nums [i] = nums [j],并且 i 和 j 的差的绝对值最大为 k. 示 ...

  5. Java实现 LeetCode 82 删除排序链表中的重复元素 II(二)

    82. 删除排序链表中的重复元素 II 给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字. 示例 1: 输入: 1->2->3->3->4- ...

  6. leetcode 83. 删除排序链表中的重复元素 及 82. 删除排序链表中的重复元素 II

    83. 删除排序链表中的重复元素 问题描述 给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次. 示例 1: 输入: 1->1->2 输出: 1->2 示例 2: 输入: ...

  7. 【每日算法】存在重复元素 II

    题目描述 这是 LeetCode 上的 219. 存在重复元素 II, 难度为 [简单] 给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,使得 nums [i] = nu ...

  8. LeetCode-082-删除排序链表中的重复元素 II

    删除排序链表中的重复元素 II 题目描述:存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除链表中所有存在数字重复情况的节点,只保留原始链表中 没有重复出现 的数字. 返回同样按升序 ...

  9. Leetcode 存在重复元素 (219,220)

    219. 存在重复元素 II 给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,使得 nums [i] = nums [j],并且 i 和 j 的差的绝对值最大为 k. / ...

  10. JS数组去掉重复元素

    JS数组去掉重复元素,这里提供3中写法. var arr =[1,2,3,4,5,6,3,4,7,2,4,1,8]; 输出:[1,2,3,4,5,6,7,8]; 1.使用indexOf() arr.i ...

随机推荐

  1. Redis 中bitMap使用及实现访问量

    1. Bitmap 是什么 Bitmap(也称为位数组或者位向量等)是一种实现对位的操作的'数据结构',在数据结构加引号主要因为: Bitmap 本身不是一种数据结构,底层实际上是字符串,可以借助字符 ...

  2. React报错之ref返回undefined或null

    正文从这开始~ 总览 当我们试图在其对应的DOM元素被渲染之前访问其current属性时,React的ref通常会返回undefined或者null.为了解决该问题,可以在useEffect钩子中访问 ...

  3. C++ 语法结构--堆

    1.堆介绍 「堆 heap」是一种满足特定条件的完全二叉树,主要可分为图 8-1 所示的两种类型. 「大顶堆 max heap」:任意节点的值 其子节点的值. 「小顶堆 min heap」:任意节点的 ...

  4. 在Windows 版的Chrome中切换标签页

    按住Ctrl+tab键之后使用page up/ page down键左右移动!

  5. [转帖]Kafka中offsets.retention.minutes和log.retention.minutes之间的区别

    https://www.cnblogs.com/lestatzhang/p/10771115.html 前言 在Kafka中,我们可能会发现两个与retention相关的配置: log.retenti ...

  6. [转帖]在KingbaseES数据库中批量创建数据库/表

    1. 问题 如何在KingbaseES中批量创建表和库? 2. 通过shell脚本文件实现 有时候我们在进行测试的时候需要进行批量的建库以及建表,这时我们可以使用shell脚本实现或者是SQL实现,s ...

  7. [转帖]Redhat、CentOS添加静态路由的方法

    https://www.diewufeiyang.com/post/1174.html 我们经常遇到需要在系统默认路由的基础上,额外添加静态路由的需求.为了使得下次系统启动这些静态路由依旧生效,我们可 ...

  8. [转帖]java -D参数设置系统属性无效问题及解决

    https://www.jb51.net/article/271236.htm   这篇文章主要介绍了java -D参数设置系统属性无效问题及解决方案,具有很好的参考价值,希望对大家有所帮助.如有错误 ...

  9. 【转帖】MySQL 8.0 hash join有重大缺陷?

    我并不这么看. 友情提醒:本文建议在PC端阅读. 徐春阳老师发文爆MySQL 8.0 hash join有重大缺陷. 文章核心观点如下:多表(比如3个个表)join时,只会简单的把表数据量小的放在前面 ...

  10. killall 以及 pkill 等命令

    https://zhidao.baidu.com/question/1500084252693125099.html // 通过 killall 命令killall nginx// 通过 pkill ...