feat: 数据结构栈 20,71,150,155,224
This commit is contained in:
parent
e0ea622d45
commit
bd018e32ea
114
top-interview-leetcode150/stack/150逆波兰表达式.js
Normal file
114
top-interview-leetcode150/stack/150逆波兰表达式.js
Normal 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];
|
||||
}
|
48
top-interview-leetcode150/stack/155最小栈.js
Normal file
48
top-interview-leetcode150/stack/155最小栈.js
Normal 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()
|
||||
*/
|
34
top-interview-leetcode150/stack/20有效的括号.js
Normal file
34
top-interview-leetcode150/stack/20有效的括号.js
Normal 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;
|
||||
}
|
55
top-interview-leetcode150/stack/224基本计算器.js
Normal file
55
top-interview-leetcode150/stack/224基本计算器.js
Normal 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*-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;
|
||||
}
|
35
top-interview-leetcode150/stack/71简化路径.js
Normal file
35
top-interview-leetcode150/stack/71简化路径.js
Normal 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('/')}`;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user