壹 ❀ 引

最近一周的工作压力很大...一周的时间一直在处理一个APP漏洞问题,因为项目三年无人维护,突然要改东西光是修改构建错误以及三方包依赖错误就花了三天时间= =。不过好在问题到已经结束尾,闲下来还是记录下最近的解题思路,本题来自LeetCode496. 下一个更大元素 I,难度属于简单,题目描述如下:

给你两个 没有重复元素 的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集。

请你找出 nums1 中每个元素在 nums2 中的下一个比其大的值。

nums1 中数字 x 的下一个更大元素是指 x 在 nums2 中对应位置的右边的第一个比 x 大的元素。如果不存在,对应位置输出 -1 。

示例 1:

输入: nums1 = [4,1,2], nums2 = [1,3,4,2].
输出: [-1,3,-1]
解释:
对于 num1 中的数字 4 ,你无法在第二个数组中找到下一个更大的数字,因此输出 -1 。
对于 num1 中的数字 1 ,第二个数组中数字1右边的下一个较大数字是 3 。
对于 num1 中的数字 2 ,第二个数组中没有下一个更大的数字,因此输出 -1 。

示例 2:

输入: nums1 = [2,4], nums2 = [1,2,3,4].
输出: [3,-1]
解释:
对于 num1 中的数字 2 ,第二个数组中的下一个较大数字是 3 。
对于 num1 中的数字 4 ,第二个数组中没有下一个更大的数字,因此输出 -1 。

提示:

1 <= nums1.length <= nums2.length <= 1000

0 <= nums1[i], nums2[i] <= 104

nums1和nums2中所有整数 互不相同

nums1 中的所有整数同样出现在 nums2 中

贰 ❀ 暴力解法

简单提取下题目中的信息,首先数组nums1是数组nums2的子集,也就是子数组,且数组中不存在重复元素。题目要求是每次取nums1中的一个元素,去nums2中的与之相同的这个元素开始找,看看还有没有比它大的元素,如果有,记录这个元素,如果没有则记录为-1,最终返回包含这些结束的数组。

思路其实就很清晰了,我们可以先遍历nums1每次取一个元素,并查询此元素在nums2中的索引,那么nums2就可以从此索引+1的位置开始遍历,判断还有没有比num1[i]更大的元素,有就记录,无就记录-1即可。

大家可以先根据这个思路自己实现代码,毕竟还是有些细节需要自己感受,下面附上代码:

/**
* @param {number[]} nums1
* @param {number[]} nums2
* @return {number[]}
*/
var nextGreaterElement = function (nums1, nums2) {
let res = [];
// 遍历nums1,每次取一个出来
for (let i = 0; i < nums1.length; i++) {
// 查找nums1[i]在nums2中的位置
let index = nums2.indexOf(nums1[i]);
// 需要注意的是这个元素可能是nums2中的最后一个,如果是最后一个就无法满足nums2的遍历条件,因此直接加入-1
if (index === nums2.length - 1) {
res.push(-1);
};
// 从查找到的索引+1位开始查找,有记录更大元素,无则记录-1
for (let j = index + 1; j < nums2.length; j++) {
if (nums2[j] > nums1[i]) {
res.push(nums2[j]);
// 因为无重复元素,找到更大的就跳出循环
break;
} else if (j === nums2.length - 1) {
res.push(-1)
};
};
};
return res;
};

代码注释写的非常清楚,这里就不多解释了,我们接下来看看官方推荐的解题思路。

贰 ❀ 单调栈思路

当然,我在看单调栈题解之前,肯定是不知道这个思路以及做法的,这里的想法自然也是来自题解区的各位大佬。其中我在看到了LeetCode用户labuladong题解时觉得非常的形象也较好理解,这里借鉴下。

比如我们要求数组[2,1,2,4,3]中每个元素的下一个更大数,其实答案很明显是[4,2,4,-1,-1],要做到求出此结果,我们一样可以从当前元素往后遍历,一直遇到第一个比自己大的元素,若遍历完还没遇到就记录为-1级了。

其实有个很有趣的比喻就是,我们可以将数组中的每个元素理解为不同身高的人,然后每个人往后看,那么这个人的视线一定是被第一个比自己高的人给挡住,用图表示就是:

那么怎么用程序表示这个过程呢?这里就得借用单调栈了。我们都知道栈有FILO(先进后出)的特性。所谓单调栈,即是入栈时经过某种处理,让入栈后的元素满足单调递增或者单调递减的顺序,说直白点就是有序的栈。可能说到这还是比较模糊,我们直接针对[2,1,2,4,3]的例子给出一个代码,再去分析,我想这样会好理解一点,代码如下:

var nextGreaterElement = function (nums) {
let res = [];
let stack = [];
// 因为栈是先进后出,所以倒序遍历反而满足了正向使用
// 对应到比身高里面,我们都是往后看,那就从最后一个开始,一个接一个往后看,这也也便于知道谁是第一个比自己高的人
for (let i = nums.length - 1; i >= 0; i--) {
// 如果栈不为空,且栈顶第一个元素(对应到数组中最后一个元素)比当前nums[i]还矮就得弹出去
while (stack.length && stack[stack.length - 1] <= nums[i]) {
// 比nums[i]矮的人都弹出去
// 因为nums是倒序遍历,每个人都是往后看比身高,假设nums[i]比后面的人还高,那么nums[i]前面的人一定只能看到
// 自己,而看不到nums[i]后面的人,那么记录后面的人是没意义的,因为反正都会被nums[i]挡住,因此统统弹出去不记录。
stack.pop();
};
// 记录第i个比自己更高的人
// 因为上面的逻辑中,比nums[i]矮的人会被全部弹出栈,最坏的情况就是没有比自己高的,栈被清空了,因此为-1;
// 而如果遇到比自己高的人是会停止弹出操作,所以栈顶第一个人一定是比自己高的人,也就是数组中最后一个人。
res[i] = stack.length ? stack[stack.length - 1] : -1;
// 记录nums[i],因为他可能是前面的人的第一个遇到比自己高的人,不高也没关系,反正会被弹出去
stack.push(nums[i]);
};
return res;
};

大家可以尝试运行下这段代码,画图理解下,不理解没关系,下面我会画图解释这个过程:

第一次我们拿到了3,用3跟栈中做比较,结果栈是空的,没有能跟自己比较身高的人,行吧,那么3对应索引的答案就是-1,同时我们得把3加入栈中,因为很可能3是前面的人中,某个人能看到的第一个比自己高的人。

第二次比较,我们取到了4,一比较结果3还没4高,由于4是站在3前面的,4更前面的人最差就是看到自己,因为3被挡住了所以不可能被其他人看到,我们将3弹出去,此时栈又是空,对应的答案我们记录为-1,同时将4加入栈中,因为4可能是前面的人能看到的第一个更高的人。

第三次比较,我们取到了2,结果一比较2没有4高,由于栈此时不为空,那么栈顶就是比2高的人,答案记录为4,同时我们还是要将2加入栈,因为2也可能是前面的人能看到的第一个比自己高的人。

第四次比较,我们取到了1,它的情况跟第三次比较相同,没有2大,所以栈不为空,栈顶第一个就是比1大的人,记录答案为2,同时把1加入栈。

第五次比较,我们取到了2,跟栈顶第一个元素1进行比较,结果发现1没有2高,因此我们将1弹出栈。此时栈不为空,还有其他人可以继续和2比较,我们继续比,此时的栈顶是2,但2不能说比2更高,我们又得把栈顶的2弹出去,此时栈仍然不为空,继续比较。此时栈顶成了4,哎,4比2要大,那么答案记录为4,同时将2加入栈.

第六次比较已经不满足循环条件,跳出循环,我们得到了对应数组中每个元素的更大元素。

OK,那么到这里,我们详细介绍了借用单调栈解决上述例子的问题,那么如果解决文中的题目呢?其实仍然是一样的思路,题目中nums1nums2的子集,只要我们能得出nums2中每个比自己大的元素并记录起来,再遍历nums1不就能直接得出答案了吗?

直接上代码:

var nextGreaterElement = function (nums1, nums2) {
let res = [];
let stack = [];
// 用于记录nums2中每个比自己大的值
let map = new Map();
for (let i = nums2.length - 1; i >= 0; i--) {
while (stack.length && stack[stack.length - 1] <= nums2[i]) {
stack.pop();
};
// 记录i对应的值,便于后续nums1查找
map.set(nums2[i], stack.length ? stack[stack.length - 1] : -1);
stack.push(nums2[i]);
};
for (let i = 0; i < nums1.length; i++) {
// 直接根据key取结果就好了
res.push(map.get(nums1[i]));
};
return res;
};

那么到这里本文结束。

JS Leetcode 496. 下一个更大元素 I 更清晰的图解单调栈做法的更多相关文章

  1. LeetCode 496. 下一个更大元素 I(Next Greater Element I) 35

    496. 下一个更大元素 I 496. Next Greater Element I 题目描述 给定两个没有重复元素的数组 nums1 和 nums2,其中 nums1 是 nums2 的子集.找到  ...

  2. Java实现 LeetCode 496 下一个更大元素 I

    496. 下一个更大元素 I 给定两个没有重复元素的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集.找到 nums1 中每个元素在 nums2 中的下一个比其大的值. nu ...

  3. Leetcode 496. 下一个更大元素 I

    1.题目描述 给定两个没有重复元素的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集.找到 nums1 中每个元素在 nums2 中的下一个比其大的值. nums1 中数字  ...

  4. 【LeetCode】496.下一个更大元素I

    496.下一个更大元素I 知识点:栈:HashMap: 题目描述 给你两个 没有重复元素 的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集. 请你找出 nums1 中每个元 ...

  5. LeetCode 556. 下一个更大元素 III(Next Greater Element III)

    556. 下一个更大元素 III 556. Next Greater Element III 题目描述 给定一个 32 位正整数 n,你需要找到最小的 32 位整数,其与 n 中存在的位数完全相同,并 ...

  6. LeetCode 503. 下一个更大元素 II(Next Greater Element II)

    503. 下一个更大元素 II 503. Next Greater Element II 题目描述 给定一个循环数组(最后一个元素的下一个元素是数组的第一个元素),输出每个元素的下一个更大元素.数字 ...

  7. LeetCode:下一个更大元素I【31】

    LeetCode:下一个更大元素I[31] 题目描述 给定两个没有重复元素的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集.找到 nums1 中每个元素在 nums2 中的 ...

  8. 496. 下一个更大元素 I

    496. 下一个更大元素 I 给定两个 没有重复元素 的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集.找到 nums1 中每个元素在 nums2 中的下一个比其大的值. ...

  9. Leetcode 503. 下一个更大元素 II

    1.题目描述 给定一个循环数组(最后一个元素的下一个元素是数组的第一个元素),输出每个元素的下一个更大元素.数字 x 的下一个更大的元素是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应 ...

  10. Leetcode---栈系列刷题(python3实现)----#496 下一个更大元素I

    给定两个没有重复元素的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集.找到 nums1 中每个元素在 nums2 中的下一个比其大的值. nums1 中数字 x 的下一个更 ...

随机推荐

  1. pojo层、dao层、service层、controller层的作用

    分层解耦介绍 1.pojo层(model) 实体层 数据库在项目中的类 model是模型的意思,与entity.domain.pojo类似,是存放实体的类. 类中定义了多个类属性,并与数据库表的字段保 ...

  2. 例2.8 已知带头结点单链表L,设计算法实现:以表中第一元素作为标准,将表中所有值小于第一个元素的结点均放在第一结点之前,所有值大于第一元素的结点均放在第一元素结点之后。

    1.题目 例2.8已知带头结点单链表L,设计算法实现:以表中第一元素作为标准,将表中所有值小于第一个元素的结点均放在第一结点之前,所有值大于第一元素的结点均放在第一元素结点之后. 2.算法分析 3.代 ...

  3. VUE - 配置跨域

    '/api': { target: 'http://localhost:8088/', //这里后台的地址模拟的;应该填写你们真实的后台接口 changOrigin: true, //允许跨域 pat ...

  4. java - 类属性 初始化的三种形式及顺序

    package chushihua; public class Hello { public static String name = getValue("属性"); { name ...

  5. [转帖]TiDB + TiFlash : 朝着真 HTAP 平台演进

    https://zhuanlan.zhihu.com/p/80495479 作者介绍:韦万,PingCAP 数据库研发工程师,主要领域是数据库的存储引擎研发,以及系统性能优化. 一.为什么我们需要 H ...

  6. Grafana监控minio的极简方法

    Grafana监控minio的极简方法 背景 想监控一下minio的部分信息. 使用过程中需要关注的内容挺多的. 只看简单的node感觉已经不够了. 所以想监控易一下. 方式和方法 minio其实集成 ...

  7. Jmeter学习之五_跟踪被测试服务器的performance

    Jmeter学习之五_跟踪被测试服务器的performance 背景 这几天简单学习了一些基本的测试过程. 可以实现一些简单基本的功能了. 今天晚上继续进行了jmeter的一些学习. 想着可以在测试人 ...

  8. [转帖]linux时间戳转换成时间指令_时间戳转换公式

    原文地址:http://wanping.blogbus.com/logs/28663569.html 1.时间戳转换为正常显示的时间格式 Freebsd 系统下: 转换命令为: date -r 111 ...

  9. [转贴]win10临时修改、永久cmd 编码格式的方法

    https://www.jianshu.com/p/40a9fbaf1cac   cmd 前言 有时候,运行一些命令行程序某些字符无法正常显示,常见的就是方块,或者是火星文字都是由于 cmd 程序的默 ...

  10. 手把手带你开发starter,点对点带你讲解原理

    京东物流 孔祥东 _____ _ ____ _ / ____| (_) | _ \ | | | (___ _ __ _ __ _ _ __ __ _| |_) | ___ ___ | |_ \___ ...