昨天用栈实现队列,队列实现栈,那今天就是栈的经典应用了
昨天学习了栈和队列的相关知识,并用栈实现队列,队列实现栈,那今天就是栈的经典应用了。在今天,我用了三道题来加强对栈的认识,这三道题都是非常经典的。同时,在做题的时候我们也要总结遇到怎么的题型,要求才可以使用栈来解决,一般来说,只要有关对称和匹配的题目,我们都可以考虑用栈来尝试答题。
相关题目:
● 20. 有效的括号:https://leetcode.cn/problems/valid-parentheses/
● 1047. 删除字符串中的所有相邻重复项:https://leetcode.cn/problems/remove-all-adjacent-duplicates-in-string/
● 150. 逆波兰表达式求值:https://leetcode.cn/problems/evaluate-reverse-polish-notation/
关于20. 有效的括号
题目描述:
● 给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串,判断字符串是否有效。
● 有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
● 示例 1: 输入: “()” 输出: true
● 示例 2: 输入: “()[]{}” 输出: true
● 示例 3: 输入: “(]” 输出: false
● 示例 4: 输入: “([)]” 输出: false
● 示例 5: 输入: “{[]}” 输出: true
该题的思路:
● 遇到匹配类型的题目,我们就要想到用栈解决,题目要求判断括号是否匹配,那我们就可以将括号一个一个压入栈,并进行判断是否匹配
● 即遍历字符串,将取出的字符进行判断入栈,这里有一个巧妙的方式
● 即如果当前遍历的字符是( { [ 中的一个,则将对应匹配的符号) } ]压入栈
● 若不是( { [ 中的一个,则将当前字符和栈顶元素进行比较,相同则弹出栈顶元素,进行下一轮比较,不同直接return false即可
● 若可以顺利遍历完,则判断栈是否为空,空return true,不空return false,目的是排除有多余括号的情况,例如:({[]}}中第一个 ( 多余,({[]}))中最后一个 )多余
代码实现
class Solution {
public boolean isValid(String s) {
//使用一个栈存放遍历过的符号
Stack<Character> characterStack = new Stack<>();
char aChar;
//循环遍历字符串,临时放到aChar进行比较操作
for (int i = 0; i < s.length(); i++) {
aChar = s.charAt(i);
//进行比较,若当前字符是( { [ 中的一个,则将对应匹配的符号) } ]压入栈
//这样有利于前后匹配,只要不是( { [中的一个,我们只需要将当前字符aChar与栈顶元素比较是不是相同即可
if (aChar == '(') {
characterStack.push(')');
} else if (aChar == '{') {
characterStack.push('}');
} else if (aChar == '[') {
characterStack.push(']');
} else {
//若不是( { [中的一个,并且当前栈为空,则一定是错的,比如字符串 "]{()}"
if (characterStack.isEmpty()) {
return false;
}
//若不是( { [中的一个,且当前栈不为空,则进行比较,若相同,则将栈顶元素弹出
//若不同,则直接返回false即可
if (characterStack.peek() != aChar) {
return false;
} else {
characterStack.pop();
}
}
}
//最后判断栈是否为空,空才是正确的
if (characterStack.isEmpty()) {
return true;
} else {
return false;
}
}
}
关于1047. 删除字符串中的所有相邻重复项
题目描述:
给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。
在 S 上反复执行重复项删除操作,直到无法继续删除。
在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。
示例:
● 输入:“abbaca”
● 输出:“ca”
● 解释:例如,在 “abbaca” 中,我们可以删除 “bb” 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 “aaca”,其中又只有 “aa” 可以执行重复项删除操作,所以最后的字符串为 “ca”。
该题的思路:
● 对于该题的理解需要好好理清楚,是要删除两个相邻且相同的字母,而不是删除连续相同的字母
● 若输入abbaca,则应输出ca
● 若输入abbbaca,则应输出abaca
● 只会同时消除相邻的bb,还剩下一个b是无法消除的
● 方式一:定义一个栈,遍历字符串,判断相邻的两个元素是否相等,若当前元素和栈顶元素相同,则弹出栈顶元素,进行下一轮的循环,此时i也会+1,所以可以看作同时消除了两个元素,最后栈里留下来的元素,再反转一下即为答案。
● 方式二:也可以定义一个StringBuffer当作栈来进行操作,同时定义一个top指针标记当前的头元素,其他的思想和步骤与方式一相同
代码实现
class Solution {
public String removeDuplicates(String s) {
//定义一个栈存放字符
Stack<Character> stack = new Stack<>();
char charAt;
//遍历字符串的每个字符,判断相邻的两个元素是否相等
for (int i = 0; i < s.length(); i++) {
charAt = s.charAt(i);
//如果栈为空或者当前元素和栈顶元素不相等,则push
if (stack.size() == 0 || charAt != stack.peek()) {
stack.push(charAt);
} else {
//否则就弹出栈顶元素
stack.pop();
}
}
//构建返回的字符串,反转一下栈的元素
String str = "";
while (!stack.isEmpty()) {
str = stack.pop() + str;
}
return str;
}
}
关于150. 逆波兰表达式求值
题目描述:
● 根据 逆波兰表示法,求表达式的值。
● 有效的运算符包括 + , - , * , / 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
● 说明:整数除法只保留整数部分。 给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。
● 示例 1:输入: [“2”, “1”, “+”, “3”, " * "] 输出: 9
● 解释: 该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
● 示例 2:输入: [“4”, “13”, “5”, “/”, “+”] 输出: 6
● 解释: 该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6
该题的思路
● 了解到逆波兰表达式之后,可以判断出这也是用栈解决的经典问题,解题的步骤思想也是比较简单
● 遇到数字则将其压入栈内,遇到运算符,则取出栈内的两个元素,并和对应的运算符运算,将结果再次压入栈内
● 最后栈内剩下的数字即为要求的值
代码实现
class Solution {
public int evalRPN(String[] tokens) {
//使用LinkedList模仿栈
Deque<Integer> deque = new LinkedList<>();
//遍历,遇到数字就push进去,遇到运算符就取出数字进行运算
for (String token : tokens) {
if (token.equals("+")) {
deque.push(deque.pop() + deque.pop());
} else if (token.equals("-")) {
//减法需要特殊处理,注意顺序
deque.push(-deque.pop()+deque.pop());
} else if (token.equals("*")) {
deque.push(deque.pop()*deque.pop());
} else if (token.equals("/")) {
//除法需要特殊处理,注意顺序
Integer pop1 = deque.pop();
Integer pop2 = deque.pop();
deque.push(pop2/pop1);
}else {
//stringStack.push(token);
//需要转化为int再进行运算
deque.push(Integer.valueOf(token));
}
}
return deque.pop();
}
}
第九天结束,第十天加油!