From bd018e32ea0ccfb2690f625648202d7b690e3b2b Mon Sep 17 00:00:00 2001 From: LouisFonda Date: Sat, 19 Apr 2025 18:08:45 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=95=B0=E6=8D=AE=E7=BB=93=E6=9E=84?= =?UTF-8?q?=E6=A0=88=2020,71,150,155,224?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../stack/150逆波兰表达式.js | 114 ++++++++++++++++++ top-interview-leetcode150/stack/155最小栈.js | 48 ++++++++ .../stack/20有效的括号.js | 34 ++++++ .../stack/224基本计算器.js | 55 +++++++++ top-interview-leetcode150/stack/71简化路径.js | 35 ++++++ 5 files changed, 286 insertions(+) create mode 100644 top-interview-leetcode150/stack/150逆波兰表达式.js create mode 100644 top-interview-leetcode150/stack/155最小栈.js create mode 100644 top-interview-leetcode150/stack/20有效的括号.js create mode 100644 top-interview-leetcode150/stack/224基本计算器.js create mode 100644 top-interview-leetcode150/stack/71简化路径.js diff --git a/top-interview-leetcode150/stack/150逆波兰表达式.js b/top-interview-leetcode150/stack/150逆波兰表达式.js new file mode 100644 index 0000000..fa30593 --- /dev/null +++ b/top-interview-leetcode150/stack/150逆波兰表达式.js @@ -0,0 +1,114 @@ +/** + * https://leetcode.cn/problems/evaluate-reverse-polish-notation/?envType=study-plan-v2&envId=top-interview-150 + * @param {string[]} tokens + * @return {number} + */ +const evalRPN = function (tokens) { + +}; + +/* +根据逆波兰表达式的规则,我们只需要使用一个栈来存操作数即可,遍历tokens,如果遇到的是 '+' '-' '*' '/' +这些操作符,直接弹出两个元素right和left,之和拿left opt right,之后把计算的结果放入栈中,继续此操作 +在最后栈中只会右一个元素,这个元素就是整个波兰表达式的结果 +*/ +function f1(tokens) { + const stack = []; + let left; let + right; + for (let i = 0; i < tokens.length; i++) { + if (tokens[i] === '+') { + right = stack.pop(); + left = stack.pop(); + stack.push(left + right); + } else if (tokens[i] === '-') { + right = stack.pop(); + left = stack.pop(); + stack.push(left - right); + } else if (tokens[i] === '*') { + right = stack.pop(); + left = stack.pop(); + stack.push(left * right); + } else if (tokens[i] === '/') { + right = stack.pop(); + left = stack.pop(); + let result = left / right; + + if (result >= 0) { + result = Math.floor(result); + } else { + result = Math.ceil(result); + } + stack.push(result); + } else { + stack.push(+tokens[i]); // 转换成number + } + } + return stack.pop(); +} + +/* +优化上面的分支,right = stack.pop();left = stack.pop(); 明显冗余,所以,先判断这个token是否是操作符 +如果不是操作符那么它一定是数字字符,就把它转换成数组压入栈中,如果是操作符,就先弹出两个操作数,计数之后把它 +压入栈中,在js中触发要特殊处理,因为js不像其他语言会把小数部分去掉 +*/ +function f2(tokens) { + const stack = []; + const n = tokens.length; + for (let i = 0; i < n; i++) { + const token = tokens[i]; + if (token === '+' || token === '-' || token === '*' || token === '/') { + // 先弹出两个操作数 + const right = stack.pop(); + const left = stack.pop(); + // 根据具体操作符进行具体操作 + if (token === '+') { + stack.push(left + right); + } else if (token === '-') { + stack.push(left - right); + } else if (token === '*') { + stack.push(left * right); + } else { + // 除法操作要特殊处理 + stack.push(left / right > 0 ? Math.floor(left / right) : Math.ceil(left / right)); + } + } else { + stack.push(parseInt(token, 10)); + } + } + return stack.pop(); +} + +/* +思路和上面一致,但是不使用栈,而是使用一个数组来储存要操作的数,用一个下标指向数组的最后一个元素, +可以理解成栈顶的元素,波兰表达式的长度n一定为奇数,所以除去操作数,应该有n+1/2个数,所以数组的长度 +设置为n+1/2 +*/ +function f3(tokens) { + const n = tokens.length; + const optNums = new Array(Math.floor((n + 1) / 2)).fill(0); + let index = -1; // 指向optNums中最后一个元素 + + for (let i = 0; i < n; i++) { + const token = tokens[i]; + if (token === '+') { + index--; + optNums[index] += optNums[index + 1]; + } else if (token === '-') { + index--; + optNums[index] -= optNums[index + 1]; + } else if (token === '*') { + index--; + optNums[index] *= optNums[index + 1]; + } else if (token === '/') { + index--; + const result = optNums[index] / optNums[index + 1]; + optNums[index] = result > 0 ? Math.floor(result) : Math.ceil(result); + } else { + // token 为数字,将它存入optNums中 + index++; + optNums[index] = parseInt(token, 10); + } + } + return optNums[index]; +} diff --git a/top-interview-leetcode150/stack/155最小栈.js b/top-interview-leetcode150/stack/155最小栈.js new file mode 100644 index 0000000..3485d7c --- /dev/null +++ b/top-interview-leetcode150/stack/155最小栈.js @@ -0,0 +1,48 @@ +const MinStack = function () { + this.data = []; // 内部使用数组维护栈元素 + this.minstack = [Infinity]; // 利用栈结构维护最小元素 +}; + +/** + * @param {number} val + * @return {void} + */ +MinStack.prototype.push = function (val) { + // 判断当前插入的元素是否比栈的最小元素还小 + if (val < this.minstack[this.minstack.length - 1]) this.minstack.push(val); + this.data.push(val); +}; + +/** + * @return {void} + */ +MinStack.prototype.pop = function () { + // 判断栈顶的元素是否是最小元素,如果是,最小元素栈顶的元素也要删除 + if (this.data[this.data.length - 1] === this.minstack[this.minstack.length - 1]) { + this.minstack.pop(); + } + if (this.data.length) this.data.pop(); +}; + +/** + * @return {number} + */ +MinStack.prototype.top = function () { + if (this.data.length) return this.data[this.data.length - 1]; +}; + +/** + * @return {number} + */ +MinStack.prototype.getMin = function () { + return this.minstack[this.minstack.length - 1]; +}; + +/** + * Your MinStack object will be instantiated and called as such: + * var obj = new MinStack() + * obj.push(val) + * obj.pop() + * var param_3 = obj.top() + * var param_4 = obj.getMin() + */ diff --git a/top-interview-leetcode150/stack/20有效的括号.js b/top-interview-leetcode150/stack/20有效的括号.js new file mode 100644 index 0000000..71e6e85 --- /dev/null +++ b/top-interview-leetcode150/stack/20有效的括号.js @@ -0,0 +1,34 @@ +/** + * @param {string} s + * @return {boolean} + */ +const isValid = function (s) { + +}; + +/* +题目所给的测试用例只包含'(',')','{','}','[',']'这些字符,如果是一个有效字符则说明它一定是一对一对的出现 +也就是说s的长度是偶数,如果是奇数就一定存在一个括号没有闭合,倘若是偶数个,我们需要在遇到右括号时检查它的前一个 +括号是否是对应的左括号,如果是的话,则表明这是一对合理的括号,之后检查后一个括号,以次类推。 +*/ +function f1(s) { + // 如果长度不是偶数,一定不能正常闭合 + if (s.length % 2 !== 0) return false; + // 括号的映射表 + const map = new Map([ + [')', '('], + [']', '['], + ['}', '{'], + ]); + const stack = []; // 利用栈结构来检查括号是否整除闭合 + for (const brack of s) { + if (map.has(brack)) { + // 如果栈为空,或者栈顶括号不是对应的括号,表明不能正常闭合,返回false + if (!stack.length || stack[stack.length - 1] !== map.get(brack)) return false; + stack.pop();// 如果正常闭合,从栈中去掉这个左括号 + } else { + stack.push(brack); + } + } + return !stack.length; +} diff --git a/top-interview-leetcode150/stack/224基本计算器.js b/top-interview-leetcode150/stack/224基本计算器.js new file mode 100644 index 0000000..7c714ca --- /dev/null +++ b/top-interview-leetcode150/stack/224基本计算器.js @@ -0,0 +1,55 @@ +/** + * https://leetcode.cn/problems/basic-calculator/?envType=study-plan-v2&envId=top-interview-150 + * @param {string} s + * @return {number} + */ +const calculate = function (s) { + +}; + +/* +定义一个sign变量,表示数前面的符号,当数字收集完毕就使用这个符号操作,这个变量的默认值为+1,原因很简单, +表达式为一个整体,可以理解为一个括号,这个括号之前隐式的包含了一个+号,比如表达式 -1 + 2 -(1-2),可以 +把它看成+(-1 + 2 -(1-2)),我们还需要使用一个栈结构来保存每一层括号表示的符号,以上面的表达式为例,第一个 +括号显然为+,所以栈结构为[+1],之后这个括号下一级的数都会受它影响,比如括号里面的-1会变为1*-1,1*(+2)都 +保持不变,当遇到"("时需要把这一层的符号压入栈中,栈结构为[+1,-1],第二级的括号里面的数都会受他影响,当遇到 +)表示,这一层级的数都操作完必了,栈中无需保存这一层级的符号,直接弹出即可,这样一次遍历即可得到结果。 +*/ + +function f1(s) { + const opt = [1];// 保存每一层括号的符号,初始表达式可以看成一个整体,所以默认值为[1] + let sign = 1; // 收集数字之后,数字前面的符号 + let ret = 0; // 计数求和的结果 + + const n = s.length; // 字符串的长度,用于控制遍历 + let i = 0; // 遍历下标 + + while (i < n) { + if (s[i] === ' ') { // 遇到空格直接跳过 + i++; + } else if (s[i] === '+') { // 如果遇到加号,根据当前层级的符号更新sign + sign = opt[opt.length - 1]; + i++; + } else if (s[i] === '-') { // 如果遇到减号,和上面同理 + sign = -opt[opt.length - 1]; + i++; + } else if (s[i] === '(') { // 遇到左括号,表明层级加深,把sign压入栈中即可 + opt.push(sign); + i++; + } else if (s[i] === ')') { // 遇到右括号,表明当前层级处理完毕,把当前层级的符号弹出即可 + opt.pop(); + i++; + } else { + // 上面的情况处理完毕,在这里只有一种情况,那么就是数字本身,数字可能是多位数,所以 + // 需要一次遍历来收集 + let num = 0; // 用于收集数字 + while (i < n && s[i] >= '0' && s[i] <= '9') { + num = num * 10 + (s[i] - '0'); + i++; + } + // 数字收集完毕,通过它前面的符号sign和结果求和 + ret += sign * num; + } + } + return ret; +} diff --git a/top-interview-leetcode150/stack/71简化路径.js b/top-interview-leetcode150/stack/71简化路径.js new file mode 100644 index 0000000..eaf8722 --- /dev/null +++ b/top-interview-leetcode150/stack/71简化路径.js @@ -0,0 +1,35 @@ +/** + * https://leetcode.cn/problems/simplify-path/?envType=study-plan-v2&envId=top-interview-150 + * @param {string} path + * @return {string} + */ +const simplifyPath = function (path) { + +}; + +/* +首先通过'/'来分割path,我们会得到如下的字符串,目录名,"."当前目录,".."上级目录,如果遇到的是文件名, +就把文件名压入栈stack中,如果遇到的是.或者空字符无需做任何操作,如果遇到的是".."则需要把当前stack栈顶 +的目录弹出表示跳转到上级目录 +*/ +function f1(path) { + const paths = path.split('/'); // 使用/来分割路径 + const stack = []; // 利用栈来处理路径 + for (const p of paths) { + // if (p === '.' || p === '') continue; + // if (p === '..') { + // // 返回到上级目录 + // if (stack.length) stack.pop(); + // continue; + // } + // stack.push(p); + // 优化上面分支,实际上我们要做的就两件事情,第一:如果是".."表明要跳转到上级目录,如果一个合规 + // 的目录名,就应该压入栈中 + if (p === '..') { + if (stack.length) stack.pop(); + } else if (p !== '' && p !== '.') { + stack.push(p); + } + } + return `/${stack.join('/')}`; +}