feat: 数据结构栈 20,71,150,155,224

This commit is contained in:
LouisFonda 2025-04-19 18:08:45 +08:00
parent e0ea622d45
commit bd018e32ea
Signed by: yigencong
GPG Key ID: 29CE877CED00E966
5 changed files with 286 additions and 0 deletions

View File

@ -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];
}

View File

@ -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()
*/

View File

@ -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;
}

View File

@ -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*-11*+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;
}

View File

@ -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('/')}`;
}