20. 有效的括号
题目:
给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 每个右括号都有一个对应的相同类型的左括号。
思路:
- 把字符串转换为一个字符数组
- 设置一个栈结构用来对括号进行判断
- 分别设置两个数组,里面分别装了左右三种小括号,方便后面进行匹配
- 设置一个循环,次数为字符数组的长度
- 获取当前元素,判断是否是左括号,如果是就放进栈中
- 判断是否是右括号,如果是就判断当前栈是否为空,如果为空就返回false,或者把栈顶元素取出和右括号用下标值比对匹配,不匹配就返回false
- 最后判断栈是否为空,不为空返回false,为空返回true
代码:
/**
* @param {string} s
* @return {boolean}
*/
var isValid = function(s) {
const bracket = s.split('')
const box = []
const left = ['(','[','{']
const right = [')',']','}']
for (let i =0;i<bracket.length;i++) {
if (left.includes(bracket[i])) {
box.push(bracket[i])
continue
}
if (right.includes(bracket[i])) {
if (!box.length||left.indexOf(box.pop())!==right.indexOf(bracket[i])){
return false
}
}
}
return box.length?false:true
};
优化:
优化的代码思路非常屌,就是直接对字符串遍历,获取每一个字符,如果是左括号中的一种,就把对应的右括号放在栈里,如果是右括号的一种,就直接从栈顶取元素,判断是否一样,如果不一样就返回false 最后判断栈长度,如果不为空就返回false,否则返回true。 还有一种思路,先声明一个表,键名是左括号,键值是对应的右括号,然后遍历字符串,如果当前元素是左括号,那就放入栈中,如果当前元素不是左括号,就把栈中元素取出来作为键名获取表中的键值和当前元素匹配,如果不匹配返回false,最后判断栈是否为空,为空返回true,否则返回false
代码:
var isValid = function (s) {
const stack = [];
for (let i = 0; i < s.length; i++) {
let c = s[i];
switch (c) {
case '(':
stack.push(')');
break;
case '[':
stack.push(']');
break;
case '{':
stack.push('}');
break;
default:
if (c !== stack.pop()) {
return false;
}
}
}
return stack.length === 0;
};
// 简化版本
var isValid = function(s) {
const stack = [],
map = {
"(":")",
"{":"}",
"[":"]"
};
for(const x of s) {
if(x in map) {
stack.push(x);
continue;
};
if(map[stack.pop()] !== x) return false;
}
return !stack.length;
};
总结:
括号匹配问题是一道非常经典的栈结构问题
1047. 删除字符串中的所有相邻重复项
题目:
给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。
在 S 上反复执行重复项删除操作,直到无法继续删除。
在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。
思路:
设置一个栈结构,设置一个循环遍历字符串中的字符,判断如果栈为空,就直接把当前字符推入栈中,如果栈不为空,就把栈顶元素取出来和当前字符进行比较,如果不一样,再把当前字符推入栈中,否则弹出栈顶元素,最后返回栈转换的字符串。 写完之后感觉在对栈顶元素比较的这一步应该是做的复杂了。
代码:
/**
* @param {string} s
* @return {string}
*/
var removeDuplicates = function(s) {
const myStack = []
for (let i = 0; i<s.length;i++) {
if (myStack.length) {
if (myStack[myStack.length-1] !== s[i]) {
myStack.push(s[i])
}else myStack.pop()
}else myStack.push(s[i])
}
return myStack.join('')
};
优化:
可以用双指针模拟栈的方法。
总结:
比较简单的一个栈结构的题。
150. 逆波兰表达式求值
题目:
给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。
请你计算该表达式。返回一个表示表达式值的整数。
注意:
- 有效的算符为
'+'、'-'、'*'和'/'。 - 每个操作数(运算对象)都可以是一个整数或者另一个表达式。
- 两个整数之间的除法总是 向零截断 。
- 表达式中不含除零运算。
- 输入是一个根据逆波兰表示法表示的算术表达式。
- 答案及所有中间计算结果可以用 32 位 整数表示。
思路:
对数组进行遍历,如果当前元素是运算符,就连续两次取出栈顶元素,第一次取出的元素是右侧被运算的元素,第二次取出的元素是运算符左侧的元素,然后判断运算符进行对应的计算,得出结果后推入栈中,如果当前元素不是运算符就直接推入栈中,最后返回栈顶元素。 注意:因为数组中的元素以字符形式保存,所以要做一个隐式转换,转换为数字型;因为所有除法操作要向零截取,所以除法得到的结果要parseInt转换为整数。
代码:
/**
* @param {string[]} tokens
* @return {number}
*/
var evalRPN = function(tokens) {
const operater = ['+','-','*','/'],stack = []
for (let i = 0;i<tokens.length;i++) {
if (operater.includes(tokens[i])) {
const right = stack.pop()
const left = stack.pop()
let tmp
switch(tokens[i]) {
case '+':
tmp = +left + +right;
break;
case '-':
tmp = left - right;
break;
case '*':
tmp = left * right;
break;
case '/':
tmp = parseInt(left / right);
break;
}
stack.push(tmp)
}else stack.push(tokens[i])
console.log(stack)
}
return stack.pop()
};
优化:
看了代码随想录和我写的差不多。
总结:
考察栈的应用,比较简单。
Day11总结
今天栈的题目,做起来感觉比较简单,可能是因为自己比较熟悉,也可能是因为今天的题比较简单,期待队列的应用。