20. 有效的括号
链接
题目链接
文章链接
第一想法
由于之前知道单调栈,所以这一道题立马想到了单调栈,用stack表示单调栈,如果是"{"、"["、"(",则存储在单调栈中,如果是右括号,则看stack的栈顶元素是否相匹配,不匹配则返回false,匹配则删除栈顶元素,代码如下:
function isValid(s: string): boolean {
let stack:string[]=[]
for(let i=0;i<s.length;i++){
if(s[i]=='{'||s[i]=='['||s[i]=='(') stack.push(s[i])
if(s[i]=='}'){
if(stack[stack.length-1]=='{') stack.pop()
else return false
}
if(s[i]==')'){
if(stack[stack.length-1]=='(') stack.pop()
else return false
}
if(s[i]==']'){
if(stack[stack.length-1]=='[') stack.pop()
else return false
}
}
return stack.length===0 //这里不能直接是true 因为存在s为'['的情况
};
看完文章后的想法
文章的整体思路和我的类似,但是比我的细,而且stack存储的是右括号,我存储的是左括号,括号不匹配总共分为三种情况: 1.第一种情况,字符串里左方向的括号多余了,所以不匹配。
2.第二种情况,括号没有多余,但是括号的类型没有匹配上
3.第三种情况,字符串里右方向的括号多余了,所以不匹配
PS:图片来源于代码随想录
代码随想录中解决这三种情况的动态图:
下面是一种优化的版本:
function isValid(s: string): boolean {
let stack:string[]=[]
type stackMap={
[key:string]:string
}
let stackmap:stackMap={
'{':'}',
'[':']',
'(':')',
}
for(let value of s){
if(stackmap.hasOwnProperty(value)){
stack.push(stackmap[value])
}else if(stack?.[stack.length-1]==value) stack.pop()
else return false
}
return stack.length===0
};
思考
这道题整体不难,但是优化版本不好想,需要多次回顾。同时面对括号问题要首先想到利用单调栈来解决问题。
1047. 删除字符串中的所有相邻重复项
链接
题目链接
文章链接
第一想法
删除相邻重复的字母,这道题通过它的讲解不难看出是利用单调栈,如果栈顶元素和当前元素相同则删除栈顶元素,否则就加入栈中,最终栈中剩余的字母就是所求结果,代码如下:
function removeDuplicates(s: string): string {
let stack:string[]=[]
for(let value of s){
if(value==stack[stack.length-1]){
stack.pop()
}else stack.push(value)
}
return stack.join("")
};
看完文章后的想法
代码随想录中的解题思路与我的思路很类似,下面是代码随想录中的动态图,帮助理解:
下面是双指针的解法,一个指向栈顶,一个指向s字符串,代码如下:
function removeDuplicates(s: string): string {
let stack:string[]=[]
let top:number=-1 //指向栈顶
for(let i=0;i<s.length;i++){
if(top==-1||stack[top]!=s[i]) stack[++top]=s[i];
else {
stack.pop()
top-- //这里要-- 是因为下标要回退
}
}
return stack.join("")
};
思考
这道题也是利用单调栈来解决问题的,思路不难,主要是会模拟过程图就可以了,同时双指针的写法没想到,但是双指针的写法一定要把top指针想清楚,因为删除相邻相同的字母时top要回退
150. 逆波兰表达式求值
链接
文章链接
题目链接
第一想法
还是单调栈,如果进入的数字则直接进入,如果是运算符号,则取出栈顶的两个元素进行运算,逆波兰表达式一般都用单调栈求解,代码如下:
function evalRPN(tokens: string[]): number {
let stack:number[]=[]
for(let s of tokens){
if(!isNaN(Number(s))){
stack.push(Number(s))
}else{
let num1:number=stack.pop()!
let num2:number=stack.pop()!
if(s=='*') stack.push(num1*num2);
if(s=='/') stack.push(~~(num2/num1));
if(s=='+') stack.push(num1+num2);
if(s=='-') stack.push(num2-num1);
}
}
return stack[0]
};
看完文章后的想法
文章的想法和我的想法完全一致,所以不再多赘述,代码随想录中有张图片很好的可以表达这个过程:
ts还有一种高级的写法:
function evalRPN(tokens: string[]): number {
let stack:number[]=[]
let map:Map<string,(a:number,b:number)=>number>=new Map([
['*',(a,b)=>a*b],
['/',(a,b)=>Math.trunc(a/b)],
['+',(a,b)=>a+b],
['-',(a,b)=>a-b],
])
for(let s of tokens){
if(!isNaN(Number(s))){
stack.push(Number(s))
}else{
let num1:number=stack.pop()!
let num2:number=stack.pop()!
//看清除数与被除数
stack.push(map.get(s)!(num2,num1))
}
}
return stack[0]
};
思考
这道题不难,用单调栈很容易就解出答案,但是有个细节要知道,那个是除数哪个是被除数,但是ts的高级写法看完才懂,自己是写不出来的。
总结
今天是大年初四,哈哈哈哈还在国内,多余的就不说了,今年的题都是可以用单调栈解决的,由于之前了解过,所以比较容易上手。今日学习2小时。