leetcode刷题(javaScript)——栈相关场景题总结

news/发布时间2024/9/20 8:49:09

 在LeetCode刷题中,栈是一个非常有用的数据结构,可以解决许多问题,包括但不限于以下几类问题:

  1. 括号匹配问题:例如检查括号序列是否有效、计算表达式的值等。
  2. 逆波兰表达式求值:使用栈来实现逆波兰表达式的计算。
  3. 柱状图中最大矩形:通过维护一个递增栈来解决柱状图中最大矩形的问题。
  4. 简化路径:使用栈来简化给定的文件路径。
  5. 二叉树的遍历:使用栈来实现二叉树的前序、中序、后序遍历等。

栈的主要思想是“后进先出”(Last In First Out,LIFO),即最后进入栈的元素最先出栈。栈通常用于解决需要“回溯”或“撤销”操作的问题,以及需要维护“最近相关性”的问题。

对于新手刷题,以下是一些建议:

  1. 掌握基本操作:首先要熟练掌握栈的基本操作,包括入栈、出栈、获取栈顶元素等。
  2. 理解栈的应用场景:了解栈在解决问题中的常见应用场景,例如上面提到的几类问题。
  3. 练习经典问题:尝试解决一些经典的栈相关问题,例如括号匹配、逆波兰表达式求值等。
  4. 多思考、多实践:在解决问题时,多思考不同的解题思路,多实践不同类型的题目,逐渐提升对栈的理解和应用能力。

20. 有效的括号

给定一个只包括 '('')''{''}''['']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。
  3. 每个右括号都有一个对应的相同类型的左括号。

主要思路是遇到左括号推入栈

遇到右括号,将栈顶左括号移除,并和它进行对比。

不相等return false

否则继续比较,直到最后,判断是否还有没匹配上的左括号

 刚开始刷题,先看第一个版本 ,有两个地方可以优化,一个是很明显的stask.length的判断可以改写为三元表达式。另外可以用对象key:value的特性进行括号对比。

/*** @param {string} s* @return {boolean}*/
var isValid = function (s) {const stask = [];for (let item of s) {if (['(', '[', '{'].includes(item)) {stask.push(item);} else {const pre = stask.pop();if ((pre === '(' && item === ')') || (pre === '[' && item === ']') || (pre === '{' && item === '}')) {continue;} else {return false;}}}if (stask.length > 0) {return false;} else {return true;}
};

优化后的代码

使用了一个映射表 map 来存储左右括号的对应关系,这样可以使代码更加清晰易懂。同时,在判断右括号是否匹配时,直接与栈顶元素进行比较,避免了多次判断的情况,提高了效率。

/*** @param {string} s* @return {boolean}*/
var isValid = function (s) {const stask = [];const map = {'(': ')','[': ']','{': '}',};for (let char of s) {if (char in map) {stask.push(char);} else if (char != map[stask.pop()]) {return false;}}return stask.length === 0;
};

这里有个巧妙之处,可以用in判断一个属性是不是对象的属性!!

此外一些小点,如可用用for of进行字符串的遍历

 1047. 删除字符串中的所有相邻重复项

给出由小写字母组成的字符串 S重复项删除操作会选择两个相邻且相同的字母,并删除它们。

在 S 上反复执行重复项删除操作,直到无法继续删除。

在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。

 思路是使用栈来消除相邻重复的字符。当前遍历的元素和前一个栈顶元素比较,如果不等,将已经pop出的元素和当前元素入栈,继续。否则,相等,当重复的元素已经pop了,无需处理。

比较后留在栈的元素就是没有相邻重复的数组,再将数组转成字符串。使用数组的join方法。

这里相同的是对字符串操作时,使用for of进行遍历即可

/*** @param {string} s* @return {string}*/
var removeDuplicates = function (s) {let stack = [];for (let char of s) {const pre = stack.pop();if ( char != pre) {stack.push(pre);stack.push(char);}}return stack.join('');
};

 1614. 括号的最大嵌套深度

 这道题原文很长,其实主要就是求括号最大嵌套层数,可以不考虑中间出现的数字和运算符。

 思想是用栈存储左括号,当遇到右括号的时候先看栈内左括号的个数,即当前的嵌套层数,将其保存下来。然后将栈顶左括号出栈;只要统计栈的最大长度即可得到最大嵌套深度

/*** @param {string} s* @return {number}*/
var maxDepth = function (s) {let stack = [];let tempDep = 0;let dep = 0;for (let char of s) {//char是左括号if (char === '(') {stack.push(char);} else if (char === ')') {//char是右括号tempDep = stack.length;dep = tempDep > dep ? tempDep : dep;stack.pop();}}return dep;
};

 这样只用考虑左括号和右括号就好啦

 1544. 整理字符串

给你一个由大小写英文字母组成的字符串 s 。一个整理好的字符串中,两个相邻字符 s[i]s[i+1],其中 0<= i <= s.length-2 ,要满足如下条件:

  • s[i] 是小写字符,则 s[i+1] 不可以是相同的大写字符。
  • s[i] 是大写字符,则 s[i+1] 不可以是相同的小写字符。

请你将字符串整理好,每次你都可以从字符串中选出满足上述条件的 两个相邻 字符并删除,直到字符串整理好为止。

请返回整理好的 字符串 。题目保证在给出的约束条件下,测试样例对应的答案是唯一的。

注意:空字符串也属于整理好的字符串,尽管其中没有任何字符。

 思路:用栈保留不相邻大小写字符,每次用栈顶元素与当前元素比较,如果两个是相邻的大小写字符,则弹出栈顶元素。否则,当前元素入栈。

使用字符串的charCodeAt方法获取字符串的ASCII码;使用Math.abs返回相差的绝对值,大小写相差32

返回字符串,需要将stack数组转字符串,同样也用数组的join方法

/*** @param {string} s* @return {string}*/var makeGood = function (s) {let stack = [];for (let char of s) {let stackTop = stack[stack.length - 1];if (stackTop && Math.abs(char.charCodeAt(0) - stackTop.charCodeAt(0)) == 32) {stack.pop();} else {stack.push(char);}}return stack.join("");
};

682. 棒球比赛 ——逆波兰表达式求值

你现在是一场采用特殊赛制棒球比赛的记录员。这场比赛由若干回合组成,过去几回合的得分可能会影响以后几回合的得分。

比赛开始时,记录是空白的。你会得到一个记录操作的字符串列表 ops,其中 ops[i] 是你需要记录的第 i 项操作,ops 遵循下述规则:

  1. 整数 x - 表示本回合新获得分数 x
  2. "+" - 表示本回合新获得的得分是前两次得分的总和。题目数据保证记录此操作时前面总是存在两个有效的分数。
  3. "D" - 表示本回合新获得的得分是前一次得分的两倍。题目数据保证记录此操作时前面总是存在一个有效的分数。
  4. "C" - 表示前一次得分无效,将其从记录中移除。题目数据保证记录此操作时前面总是存在一个有效的分数。

请你返回记录中所有得分的总和。

 这是一道用栈来进行求值的,当前项与前一项或前两项有关系,且已经存在的元素可以被删除。就可以考虑栈对栈顶元素的操作

/*** @param {string[]} operations* @return {number}*/
const calPoints = function (operations) {const stack = [];for (let item of operations) {if (item === "C") {stack.pop();} else if (item === "D") {stack.push(stack[stack.length - 1] * 2);} else if (item === "+") {stack.push(stack[stack.length - 1] + stack[stack.length - 2]);} else {stack.push(Number(item));}}return stack.reduce((total, num) => total + num, 0);
};

 思想是用栈存储确定后的没项元素,根据当前匹配的是数字还是符号进行响应规则增加或删除项。最后用数组的reduce方法对数字求和。比较简单

 150. 逆波兰表达式求值

逆波兰表达式是一种不需要括号来标识优先级的数学表达式表示方法,它通过将操作符放在操作数的后面来表示运算顺序。

给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。

请你计算该表达式。返回一个表示表达式值的整数。

注意:

  • 有效的算符为 '+''-''*''/'
  • 每个操作数(运算对象)都可以是一个整数或者另一个表达式。
  • 两个整数之间的除法总是 向零截断
  • 表达式中不含除零运算。
  • 输入是一个根据逆波兰表示法表示的算术表达式。
  • 答案及所有中间计算结果可以用 32 位 整数表示。

 

 思路:用栈存储操作数,遇到操作符号+-*/时从栈顶依次弹出两个数,分别作为右操作数和左操作数。需要手动处理+-*/匹配时的运算。并且考虑除法向0靠拢。js的Math函数,trunc提供了这个方法。这个平时真用的挺少的。先给个初版的,后面有个进阶优化的代码

/*** @param {string[]} tokens* @return {number}*/
var evalRPN = function (tokens) {let stack = [];const symbol = ['+', '-', '*', '/'];for (let item of tokens) {if (symbol.includes(item)) {let right = stack.pop();let left = stack.pop();stack.push(getData(left, right, item));} else {stack.push(Number(item));}}return stack[0];
};
function getData(left, right, operate) {switch (operate) {case '+':return left + right;case '-':return left - right;case '*':return left * right;case '/':return Math.trunc(left / right);}}

 这里比较操作符用数组includes方法减少了比较,那如何更加匹配时的简化运算逻辑呢

考虑使用ES6提供的Map对象,key值存放符合,value值存放一个函数,接收两个操作数。

通过Map对象的has方法判断key值是否存在,通过get方法获取函数。parseInt和Number都可以转换数字,这里只是提供了一种方法

var evalRPN = function (tokens) {const stack = [];const operators = new Map([['+', (a, b) => a + b],['-', (a, b) => a - b],['*', (a, b) => a * b],['/', (a, b) => Math.trunc(a / b)]]);for (let token of tokens) {if (operators.has(token)) {let right = stack.pop();let left = stack.pop();let result = operators.get(token)(left, right);stack.push(result);} else {stack.push(parseInt(token));}}return stack[0];
};

 

844. 比较含退格的字符串

给定 st 两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回 true# 代表退格字符。

注意:如果对空文本输入退格字符,文本继续为空。

/*** @param {string} s* @param {string} t* @return {boolean}*/
var backspaceCompare = function (s, t) {return getStr(s) === getStr(t);
};function getStr(str) {let stack = [];for (let char of str) {if (char != '#') {stack.push(char);} else {stack.pop();}}return stack.join('');
}

 思路:用栈保存s或t最终的结果,在比较两者是否相等。

不等于#时push进,为#时将栈顶元素弹出。

比较简单

 2696. 删除子串后的字符串最小长度

给你一个仅由 大写 英文字符组成的字符串 s 。你可以对此字符串执行一些操作,在每一步操作中,你可以从 s 中删除 任一个 "AB""CD" 子字符串。通过执行操作,删除所有 "AB""CD" 子串,返回可获得的最终字符串的 最小 可能长度。注意,删除子串后,重新连接出的字符串可能会产生新的 "AB""CD" 子串。

/*** @param {string} s* @return {number}*/
var minLength = function (s) {const map = {'A': 'B','C': 'D'}let stack = [];for (let char of s) {if (stack.length > 0 && char === map[stack[stack.length - 1]]) {stack.pop();} else {stack.push(char);}}return stack.length;
};

也是一道典型的字符匹配题目,用栈加map存储键值对比较真的无敌了。

不啰嗦了,这里返回的是数组长度注意一下就行

 71. 简化路径

给你一个字符串 path ,表示指向某一文件或目录的 Unix 风格 绝对路径 (以 '/' 开头),请你将其转化为更加简洁的规范路径。

在 Unix 风格的文件系统中,一个点(.)表示当前目录本身;此外,两个点 (..) 表示将目录切换到上一级(指向父目录);两者都可以是复杂相对路径的组成部分。任意多个连续的斜杠(即,'//')都被视为单个斜杠 '/' 。 对于此问题,任何其他格式的点(例如,'...')均被视为文件/目录名称。

请注意,返回的 规范路径 必须遵循下述格式:

  • 始终以斜杠 '/' 开头。
  • 两个目录名之间必须只有一个斜杠 '/'
  • 最后一个目录名(如果存在)不能 '/' 结尾。
  • 此外,路径仅包含从根目录到目标文件或目录的路径上的目录(即,不含 '.''..')。

返回简化后得到的 规范路径

 

 终结题目的要求:/不能重复;..表示回退一步;.表示在当前;这道题的难点是对/的处理。要是按/进行分隔呢,只保留文件名和操作运算符。看下代码怎么实现吧

/*** @param {string} path* @return {string}*/
var simplifyPath = function (path) {let stack = [];const pathArr = path.split('/');pathArr.forEach(item => {if (item === '..') {stack.pop();} else if (item && item != '.') {stack.push(item);}})return stack.length ? '/' + stack.join('/') : '/';
};

 思路:按照/将字符分开为数组。然后用栈存储当前经过的路径上的文件夹名称。

遇到..栈顶弹出,遇到.不处理。遇到文件名,push进栈

最后将文件名按/拼接,注意以/开头

144. 二叉树的前序遍历

(1)先(根)序遍历(根左右)

不管是前、中、后序遍历,总的来说都要从根元素开始,把左子树遍历一遍在把右子树遍历一遍,只不过过程中打印节点顺序不同而有所差异。栈的思想是当前元素遍历到了,但是它的左子树或右子树没遍历完,需要入栈,当左子树或右子数遍历了即可出栈

/*** Definition for a binary tree node.* function TreeNode(val, left, right) {*     this.val = (val===undefined ? 0 : val)*     this.left = (left===undefined ? null : left)*     this.right = (right===undefined ? null : right)* }*/
/*** @param {TreeNode} root* @return {number[]}*/
var preorderTraversal = function (root) {let arr = [];let stack = [];let current = root;while (current != null || stack.length > 0) {while (current!= null) {arr.push(current.val);stack.push(current);current = current.left;}current = stack.pop();current = current.right;}return arr;
};

 arr存放遍历后的数据,优先入栈跟节点,其次左节点,最后右节点。每次循环仅得到一个节点,切记不多写,容易混乱

94. 二叉树的中序遍历

中(根)序遍历(左根右)

/*** Definition for a binary tree node.* function TreeNode(val, left, right) {*     this.val = (val===undefined ? 0 : val)*     this.left = (left===undefined ? null : left)*     this.right = (right===undefined ? null : right)* }*/
/*** @param {TreeNode} root* @return {number[]}*/
var inorderTraversal = function (root) {const arr = [];const stack = [];let current = root;while (current != null || stack.length > 0) {while (current != null) {stack.push(current);current = current.left;}current = stack.pop();arr.push(current.val);current = current.right;}return arr;
};

 入栈的顺序是一样的,只不过arr添加的顺序不同,这里先push的一定是左子树

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

如若内容造成侵权/违法违规/事实不符,请联系编程老四网进行投诉反馈email:xxxxxxxx@qq.com,一经查实,立即删除!

相关文章

5GC SBA架构

协议标准&#xff1a;Directory Listing /ftp/Specs/archive/23_series/23.501/ (3gpp.org) NF描述说明NSSFNetwork Slice Selection Function网络切片选择&#xff0c;根据UE的切片选择辅助信息、签约信息等确定UE允许接入的网络切片实例。NEF Network Exposure Function网络开…

Ainx的全局配置

&#x1f4d5;作者简介&#xff1a; 过去日记&#xff0c;致力于Java、GoLang,Rust等多种编程语言&#xff0c;热爱技术&#xff0c;喜欢游戏的博主。 &#x1f4d7;本文收录于Ainx系列&#xff0c;大家有兴趣的可以看一看 &#x1f4d8;相关专栏Rust初阶教程、go语言基础系列…

mysql,for循环执行sql

遇到一个问题&#xff0c;我需要模拟上百万数据来优化sql&#xff0c;线上数据down不下来&#xff0c;测试库又没有&#xff0c;写代码执行要么慢要么就是sql语句太长。 于是&#xff0c;直接用mysql自带的功能去实现&#xff01; 简单而简单 mysql可以for循环&#xff1f;没…

Docker 第十九章 : 阿里云个人镜像仓使用

Docker 第十九章 : 阿里云个人镜像仓使用 本章知识点: 如何创建镜像库,如何设置密码,如何登录与退出个人镜像仓,如何本地打镜像,如何将本地镜像推送到个人镜像库。 背景 在项目YapiDocker部署中,因读取mongo:latest 版本不一致,导致后续执行步骤的异常。遇到此场景…

Python中网络请求超时的原因及解决方案

在进行网络数据爬取过程中&#xff0c;网络请求超时是一个令人头疼的问题。尤其在Python中&#xff0c;我们常常需要应对各种网络爬虫、API调用或其他网络操作&#xff0c;而网络请求超时的原因千奇百怪。在本篇文章中&#xff0c;我们将深入了解网络请求超时的可能原因&#x…

MATLAB环境下基于局部高斯分布拟合能量的图像分割方法

局部高斯分布拟合能量模型利用局部图像灰度均值和方差信息构造能量泛函&#xff0c;能量泛函由局部图像轮廓内外的高斯分布拟合项和正则项构成&#xff0c;拟合项驱使演化曲线向目标轮廓演化&#xff0c;正则项保持演化曲线的光滑度和避免重新初始化水平集函数。局部高斯分布拟…

拦截器Interceptor(黑马学习笔记)

学习完了过滤器Filter之后&#xff0c;接下来我们继续学习拦截器Interceptor。 拦截器我们主要分为三个方面进行讲解&#xff1a; 1.介绍下什么是拦截器&#xff0c;并通过快速入门程序上手拦截器 2.拦截器的使用细节 3.通过拦截器Interceptor完成登录校验功能 我们先学习第一…

LNMP架构介绍及配置--部署Discuz社区论坛与wordpress博客

一、LNMP架构定义 1、LNMP定义 LNMP&#xff08;Linux Nginx Mysql Php&#xff09;是指一组通常一起使用来运行动态网站或者服务器的自由软件名称首字母缩写&#xff1b;Linux系统下NginxMySQLPHP这种网站服务器架构。 Linux是一类Unix计算机操作系统的统称&#xff0c;是目…

【数据结构】归并排序

算法描述 归并排序&#xff08;MERGE-SORT&#xff09;是建立在归并操作上的一种有效的排序算法,该算法是采用分治法&#xff08;Divide andConquer&#xff09;的一个非常典型的应用。将已有序的子序列合并&#xff0c;得到完全有序的序列&#xff1b;即先使每个子序列有序&a…

【嵌入式——QT】数值输入和显示组件

数值输入和显示组件 QSlider&#xff1a;滑动条&#xff0c;通过滑动来设置数值&#xff1b;QScrollBar&#xff1a;卷滚条&#xff0c;与QSlider类似&#xff0c;还可以用于卷滚区域&#xff1b;QProgressBar&#xff1a;进度条&#xff0c;一般用于显示任务进度&#xff0c;…

YOLOv9-Openvino和ONNXRuntime推理【CPU】

1 环境&#xff1a; CPU&#xff1a;i5-12500 Python&#xff1a;3.8.18 2 安装Openvino和ONNXRuntime 2.1 Openvino简介 Openvino是由Intel开发的专门用于优化和部署人工智能推理的半开源的工具包&#xff0c;主要用于对深度推理做优化。 Openvino内部集成了Opencv、Tens…

【C++从0到王者】第四十六站:图的深度优先与广度优先

文章目录 一、图的遍历二、广度优先遍历1.思想2.算法实现3.六度好友 三、深度优先遍历1.思想2.代码实现 四、其他问题 一、图的遍历 对于图而言&#xff0c;我们的遍历一般是遍历顶点&#xff0c;而不是边&#xff0c;因为边的遍历是比较简单的&#xff0c;就是邻接矩阵或者邻接…

AI:136-基于深度学习的图像生成与风格迁移

🚀点击这里跳转到本专栏,可查阅专栏顶置最新的指南宝典~ 🎉🎊🎉 你的技术旅程将在这里启航! 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 ✨✨✨ 每一个案例都附带关键代码,详细讲解供大家学习,希望…

【iOS ARKit】协作 Session 实例

协作 Session 使用注意事项 协作 Session 是在 ARWorldMap 基础上发展起来的技术&#xff0c;ARWorldMap 包含了一系列的地标、ARAnchor 及在观察这些地标和 ARAnchor 时摄像机的视场&#xff08;View&#xff09;。如果用户在某一个位置新创建了一个 ARAnchor&#xff0c;这时…

qtcreator-ros 安装记录

文章目录 ros_qtc_pluginros_qt_demo参考链接ros_qtc_plugin ROS Qt Creator 插件是专门为 ROS 开发的,通过简化任务和为 ROS 工具创建集中位置来提高开发人员的效率。由于它建立在Qt Creator平台之上,用户可以访问其所有现有功能,例如:语法高亮,代码索引,编辑器(C++,…

团结引擎——DotNet Wasm方案

参考&#xff1a;团结引擎 DotNet WebAssembly(Wasm) 介绍 一、当前编译流程 通过IL2CPP将C#转成C/C&#xff1b;通过Emscripen将C/C转成WebAssembly&#xff1b; 二、 当前存在问题 IL2CPP在处理类似泛型、反射结构时&#xff0c;由于缺少运行时信息&#xff0c;必须全量生…

qt-C++笔记之使用QProcess去执行一个可执行文件时指定动态库所存放的文件夹lib的路径

qt-C笔记之使用QProcess去执行一个可执行文件时指定动态库所存放的文件夹lib的路径 参考博文&#xff1a; 1.C笔记之执行一个可执行文件时指定动态库所存放的文件夹lib的路径 2.Linux笔记之LD_LIBRARY_PATH详解 3.qt-C笔记之使用QProcess去执行一个可执行文件时指定动态库所存放…

13.网络游戏逆向分析与漏洞攻防-网络通信数据包分析工具-如果没有工具就创造工具

内容参考于&#xff1a; 易道云信息技术研究院VIP课 上一个内容 &#xff1a;12.游戏网络通信存在的问题 现在把游戏网络的架构看了一个小小的大概&#xff0c;可以用它的接口发数据接收数据了&#xff0c;如果真正想用它这一套东西&#xff0c;真正核心不在于它的接口而在于…

thinkphp6定时任务

这里主要是教没有用过定时任务没有头绪的朋友, 定时任务可以处理一些定时备份数据库等一系列操作, 具体根据自己的业务逻辑进行更改 直接上代码 首先, 是先在 tp 中的 command 方法中声明, 如果没有就自己新建一个, 代码如下 然后就是写你的业务逻辑 执行定时任务 方法写好了…

数据结构之二叉树的精讲

&#x1d649;&#x1d65e;&#x1d658;&#x1d65a;!!&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦ &#x1f44f;&#x1f3fb;‧✧̣̥̇:Solitary_walk ⸝⋆ ━━━┓ - 个性标签 - &#xff1a;来于“云”的“羽球人”。…
推荐文章