目录
20. 有效的括号(栈实现)
讲完了栈实现队列,队列实现栈,接下来就是栈的经典应用了。
大家先自己思考一下 有哪些不匹配的场景,在看视频 我讲的都有哪些场景,落实到代码其实就容易很多了。
题目链接/文章讲解/视频讲解:代码随想录
题解思路:
本题使用栈实现有效括号的判断,这里卡哥视频说的也很清楚为什么使用栈去实现,文字不好表述,全靠感觉!本题只需要考虑三种情况就能把所有情况都能包括:
第一种情况:当遍历完字符串,与栈顶元素判断并弹出对应右端符号后,如果栈不为空,说明字符串里左方向的括号多余了 ,所以不匹配。如果为空,那么就是有效括号;
第二种情况:在遍历右端对应符号时,栈顶元素与当前字符串遍历的字符不匹配,那说明括号类型不匹配了;
第三种情况:当在遍历字符串的时候栈中没有元素了,此时说明字符串里右方向的括号多余了;
对于以上三种情况一定要注意:必须先判断此时遍历字符串中的元素是否与栈顶元素相同后,才能进行弹出对应的右端符号操作
class Solution {
public boolean isValid(String s) {
Stack<Character> stackChar = new Stack<>();
//剪枝条件:如果字符串的个数是奇数,那肯定就不是有效括号对了
if(s.length() % 2 != 0){
return false;
}
//遍历整个字符串
for(int i = 0; i < s.length(); i++){
if(s.charAt(i) == '(') stackChar.push(')');
else if(s.charAt(i) == '{') stackChar.push('}');
else if(s.charAt(i) == '[') stackChar.push(']');//遇到左端符号时,不需要判断直接往栈中添加对应的右端符号即可,然后遍历到右端符号时,再与栈顶元素一一判断是否相同,就能判断出是否是有效括号,因此在栈中添加完左端符号对应的右端符号后,对以下三种情况进行判断即可!
//第三种情况:当在遍历字符串的时候栈中没有元素了,此时说明字符串里右方向的括号多余了;
//第二种情况:在遍历右端对应符号时,栈顶元素与当前字符串遍历的字符不匹配,那说明括号类型不匹配了
else if( stackChar.empty() || s.charAt(i) != stackChar.peek()) return false;
else stackChar.pop(); //必须先判断此时遍历字符串中的元素是否与栈顶元素相同后,才能进行弹出对应的右端符号操作
}
//第一种情况:当遍历完字符串,与栈顶元素判断并弹出对应右端符号后,如果栈不为空,说明字符串里左方向的括号多余了 ,所以不匹配。如果为空,那么就是有效括号
return stackChar.empty();
}
}
1047. 删除字符串中的所有相邻重复项
方法一:(利用栈实现)
题解思路:
本题主要分三步:
第一步:当栈为空或者栈顶元素与此时遍历的字符串不相同时,向栈中添加元素
第二步:利用栈实现消除相邻相同元素后,从栈中一个一个取出元素放入新的字符串容器中
第三步:此时从栈中取出的元素放入容器中顺序是反的,因此需要反转字符串
class Solution {
public String removeDuplicates(String s) {
Stack<Character> str = new Stack<>(); //使用栈去处理相邻元素消除的问题
for(int i = 0; i < s.length(); i++){
if(str.empty() || str.peek() != s.charAt(i)){ //当栈为空或者栈顶元素与此时遍历的字符串不相同时,向栈中添加元素
str.push(s.charAt(i));
}else{
str.pop();
}
}
StringBuffer result = new StringBuffer();
while(!str.empty()){ //利用栈实现消除相邻相同元素后,从栈中一个一个取出元素放入新的字符串容器中
result.append(str.pop());
}
return new String(result.reverse()); //此时从栈中取出的元素放入容器中顺序是反的,因此需要反转字符串
}
}
方法二:(使用双端队列)
题解思路:
使用双端队列和栈实现相邻字符串的消除的原理一致,也分为三步去实现:
第一步:当队列为空或者队列的头部元素与此时遍历的字符串不相同时,向栈中添加元素
第二步:利用栈实现消除相邻相同元素后,从栈中一个一个取出元素放入新的字符串容器中
这里可以学习到的点是:str = deque.pop() + str //这种字符串拼接可以将弹出的字符串按正序存储为字符串序列!!!!!注意与str += deque.pop()方式得到的结果不一样,这种得到的字符串还是逆序的!!!!
class Solution {
public String removeDuplicates(String s) {
Deque<Character> deque = new ArrayDeque<>(); //使用双端队列实现相邻字符串的消除
for(int i = 0; i < s.length(); i++){
if(deque.isEmpty() || deque.peek() != s.charAt(i)){ //第一步处理和栈一样,当队列为空或者队列的头部元素与此时遍历的字符串不相同时,向栈中添加元素
deque.push(s.charAt(i));
}else{
deque.pop();
}
}
String str = ""; //利用字符串拼接
while(!(deque.isEmpty())){ //由于双端队列先进先出,因此队列弹出后保存到字符串序列中顺序还是反向的,需要像栈一样还得反转字符串操作
str = deque.pop() + str; //这种字符串拼接可以将弹出的字符串按正序存储为字符串序列,注意与str += deque.pop()方式得到的结果不一样,这种得到的字符串还是逆序的
}
return str; //直接返回字符串即可
}
}
150. 逆波兰表达式求值(利用堆栈实现计算过程)
本题不难,但第一次做的话,会很难想到,所以先看视频,了解思路再去做题
题目链接/文章讲解/视频讲解:代码随想录
题解思路:
计算中运算时存储的也是逆波兰表达式,其底层实现原理就是通过堆栈的弹出参与运算的数值和压入运算的结果实现的计算过程!!!这里明确两点:第一点,逆波兰表达式:是一种后缀表达式,所谓后缀就是指运算符写在后面。平常使用的算式则是一种中缀表达式,如 ( 1 + 2 ) * ( 3 + 4 ),该算式的逆波兰表达式写法为( ( 1 2 + ) ( 3 4 + ) * ),可以发现去掉括号后表达式无歧义,上式即便写成 1 2 + 3 4 + * 也可以依据次序计算出正确结果;第二点,适合用栈操作运算的情况--遇到数字则入栈;遇到运算符则取出栈顶两个数字进行计算,并将结果压入栈中。
本题实现的过程中也需要注意以下问题:
问题一:leetcode 内置jdk的问题,不能使用==判断字符串是否相等
问题二:运算的时候一定注意数值的运算顺序,一定是后弹出的数值num2与先弹出的数值nums1进行运算的!!!
问题三:如果遇到运算符就进行运算,将堆栈中弹出的数值进行运算后,对其得到的结果值再放入堆栈中;如果没遇到运算符就把数值中的字符转换成数值类型存储到堆栈中
class Solution {
public int evalRPN(String[] tokens) {
Stack<Integer> stack = new Stack<>();
int result = 0;
for(int i = 0; i < tokens.length; i++){
String temp = tokens[i]; //leetcode 内置jdk的问题,不能使用==判断字符串是否相等
if("+".equals(temp) || "-".equals(temp) || "*".equals(temp) || "/".equals(temp)){ //如果遇到运算符就进行运算
int nums1 = stack.pop();
int nums2 = stack.pop(); //运算的时候一定注意数值的运算顺序,一定是后弹出的数值num2与先弹出的数值nums1进行运算的!!!
if("+".equals(temp)) result = nums2 + nums1;
else if("-".equals(temp)) result = nums2 - nums1;
else if( "*".equals(temp)) result = nums2 * nums1;
else if( "/".equals(temp)) result = nums2 / nums1;
stack.push(result); //将堆栈中弹出的数值进行运算后,对其得到的结果值再放入堆栈中
}else{
stack.push(Integer.valueOf(tokens[i])); //如果没遇到运算符就把数值中的字符转换成数值类型存储到堆栈中
}
}
return stack.pop(); //最后堆栈中只剩一个元素,就是运算的结果
}
}