今日题目:
- 20. 有效的括号
- 1047. 删除字符串中的所有相邻重复项
- 150. 逆波兰表达式求值
解题思想:
- 括号匹配
由于栈结构的特殊性,非常适合做对称匹配的问题。我们在解题的时候,可以利用map存储匹配的括号,当遍历遇到左括号时,把右括号压进栈,当遇到右括号时,从栈中弹出元素判断是否与遍历到的元素相等。这样毕直接左括号入栈实现要简单明了很多。注意最后还要判断栈是否为空。
- 删除字符串中的所有相邻重复项
本题复杂之处在于,不仅要招相邻的重复项,相邻的重复项删除之后,又有新的黏连项,要判断是否需要继续删除。但思路还是一样,用栈来存储判断。也可以用双指针来模拟栈,原地解题,一个指针一直指向栈顶元素,另一个指针指向遍历的元素,不断对比移动。
- 逆波兰式
逆波兰表达式相当于二叉树中的后序遍历。在本题中,每一个表达式要得到一个结果,然后拿这个结果再进行运算,遍历到数字的时候就压入栈,遍历到运算符号的时候就从栈中取数字,再把此结果压入栈。
但这道题有很多小细节需要注意,可以用Map数据格式存储运算表达式与对应的算式回调函数。这样更有利于对每个算数的情况进行细节把控:+号运算注意隐式转换为数字(防止变为字符串相加),除号还要做情况讨论,同时要注意操作数的顺序,因为是栈要倒过来。不然会出错。
代码:
- 20 . 有效的括号
var isValid = function(s) {
const map = new Map
map.set("{","}")
map.set("[","]")
map.set("(",")")
const stack = []
for(let i = 0; i< s.length; i++) {
if(map.has(s[i])) {
stack.push(map.get(s[i]))
} else {
if(s[i] !== stack.pop()) return false
}
}
if(stack.length !== 0) return false
return true
};
- 1047 . 删除字符串中的所有相邻重复项
var removeDuplicates = function(s) {
const stack = [s[0]]
let pre
for(let i = 1; i<s.length; i++) {
pre = stack.pop() || undefined
if(s[i] == pre) continue
else {
stack.push(pre)
stack.push(s[i])
pre = s[i]
}
}
return stack.join('')
};
//原地双指针
var removeDuplicates = function(s) {
s = [...s]
let top = -1 //栈顶下标
for(let i = 0; i<s.length; i++) {
if(top === -1 || s[top] !== s[i]) {
s[++top] = s[i] //入栈
} else {
top--
}
}
s.length = top+1
return s.join('')
};
- 150 . 逆波兰表达式求值
var evalRPN = function(tokens) {
const s = new Map([
["+",(a,b) => a*1 + b*1], //这里注意一定要乘以1,不然相当于字符串相加了
["-",(a,b) => b-a], //注意顺序是b-a
["*",(a,b) => b*a],
["/",(a,b) => (b/a) | 0] //这里要 |0操作,不然涉及到除法的会出错
])
const stack = []
for(const i of tokens) {
if(!s.has(i)) {
stack.push(i)
continue
}
stack.push(s.get(i)(stack.pop(), stack.pop()))
}
return stack.pop()
};
总结:
1047题双指针来模拟栈的做法还蛮有意思的,注意最后要控制数组的长度。逆波兰表达式其实不难,但是细节很多,自己做的时候一直不知道问题出现在哪,看了代码随想录给的代码确实巧妙优雅,用map不仅来存储符号,还存储对应的回调函数,并且这样操作能更好处理操作数之间的顺序、隐式转换为数字、对除法做特殊处理。