算法训练营Day11

#Java #栈和队列

开源学习资料

Feeling and experiences:

有效的括号:力扣题目链接

对于这个处理字符,括号匹配的问题,在学习数据结构中也遇到过。

数据结构中的做法就是模拟栈,利用栈先进后出的特点。

以下是我第一次写所遇到的问题:

class Solution {
    public boolean isValid(String s) {
      //先创建一个栈,来进行辅助操作
      Stack<Character> stack = new Stack<>();
      //因为括号只有三种,可以直接都表示出来
      //用左括号做参考,把右括号入栈处理
       int count =0;  //后面又加了count来解决括号数目不一致问题 ,但这样不能解决右括号比左括号多的情况
       //如果长度为奇数,则一定不行
       if(s.length()%2 == 1){
           return false;
       }
      //遇到右括号,就把右括号入栈
      for(int i =0;i<s.length();i++){
         
         char ch = s.charAt(i);
         if(ch == '('){
             stack.push(')');
             count++;
         }
         if(ch == '{'){
             stack.push('}');
             count++;
         }
         if(ch == '['){
             count++;
             stack.push(']');
         }
         //如果栈中还有元素,看当前的ch与栈顶元素是否匹配,匹配则出栈
         if(!stack.isEmpty() && ch == stack.peek()){
             stack.pop();
             count--;
         }
         //可能还会遇到右括号多了的情况(其实就是要左右括号数目要是相等的)
      }
      if(count!=0){
             return false;
         }
      return stack.isEmpty();
    }
}

此代码的注释看到很乱,因为我在第一遍写的时候,有很多问题和细节都没有考虑到。

我最开始只判断了左右括号是否匹配的问题,而没有考虑它们数目是否相等。

我的代码思路:以左括号为参考,把对应的右括号入栈,然后去判断这个右括号,如果它与当前的左括号匹配,则出栈。依次这样操作,如果栈为空,说明匹配成功,如果不为空则失败。

大体思路没问题,但里面还有很多细节没有处理。

比如还存在右括号多了的情况(因为是以左括号为参考)。

我就在基础上一步一步增删改查,加了长度判断(因为要匹配,长度为奇数肯定不行),又加了计数器,来判断左右括号数量是否一致(但是判断出现了问题)。

导致思路很混乱,越写越乱。

该代码最后还是没有解决右括号多了的问题。

还是要整理好思路在下手~

其实写完思考一下,以我的方法来写的话,就只要解决了右括号比左括号多的问题就行。

因为我是以左括号为参照,加入栈的只有右括号,而右括号入栈的数量是基于左括号的,在if判断中,遇到了一个左括号就入栈一个右括号。那如果右括号的数量比左括号多,在该代码中是体现不出来的,所以还需要一个条件判断来控制。

因为外层for循环会遍历每一个字符,在if语句中只有遇到左括号才入栈与之对应的右括号,仔细想一下,如果没有遍历到左括号了,也就不会添加右括号,那么在循环里栈为空了,就代表右括号多了。

细品以下代码:

外层for循环会依次遍历执行,遍历到左括号就会入栈一个右括号,此时栈就不是空的了,匹配了再弹出来,这个时候栈空了,但是这一次循环的语句也执行完了,又到下一次循环了。

class Solution {
    public boolean isValid(String s) {
        Stack<Character> stack = new Stack<>();
        char ch;
        for (int i = 0; i < s.length(); i++) {
            ch = s.charAt(i);
            //碰到左括号,就把相应的右括号入栈
            if (ch == '(') {
                stack.push(')');
            }else if (ch == '{') {
                stack.push('}');
            }else if (ch == '[') {
                stack.push(']');
            } else if (stack.isEmpty() || stack.peek() != ch) {
                return false;
            }else {//如果是右括号判断是否和栈顶元素匹配
                stack.pop();
            }
        }
        //最后判断栈中元素是否匹配
        return stack.isEmpty();
    }
}

因为是栈和队列的练习,就先不写map集合的解答了。

删除字符串中的所有相邻重复项:力扣题目链接

删除相邻且相同的元素很简单,但是如上述示例:删除了还要合并后再判断。(类似消消乐)

一次一次操作,那这样效率就太低了。

class Solution {
    public String removeDuplicates(String s) {
    //利用栈,先依次把字符串中的元素入栈,遇到遍历的元素与栈顶元素相同,则出栈栈顶元素
    Deque<Character> deque = new LinkedList<>();
    StringBuffer res = new StringBuffer();
    for(int i=0;i<s.length();i++){
        //用ch记录当前遍历到的字符
        char ch = s.charAt(i);
        //如果当前遍历到的字符与栈顶元素不同则入栈
        if(deque.isEmpty() || ch != deque.peek()){
            deque.push(ch);
        }
        else{
            deque.pop();
        }
    }
    //把栈中的元素倒入结果中
        while(!deque.isEmpty()){
            res.append(deque.peekLast());
            deque.pollLast();
        }
        return res.toString();
    }
}

本次我是用到了双端队列,这样就既可以用模拟栈来消除元素后,再模拟队列存放结果。

实现了栈和队列的结合使用。

注意判断栈空的情况,不然会容易抛出异常。

特别是在:if(deque.isEmpty() || ch != deque.peek()),为什么会去判断是否为空?

1. 如果栈为空,直接将当前字符入栈,因为栈中没有元素,无需比较。
2. 如果栈不为空,检查当前字符是否与栈顶元素不同,如果不同,则入栈;如果相同,则说明有相邻重复字符,出栈栈顶元素。

通过这个条件,代码能够正确处理栈为空的情况,避免了潜在的空栈异常。

逆波兰表达式求值:力扣题目链接

 该题目,最开始还没有读懂是什么意思。

后面看了代码随想录的文字讲解,又把该问题转变为了栈的运用。

在力扣的官方解答中,也有动态示意图

把题意理解到了就非常好做了

class Solution {
    public int evalRPN(String[] tokens) {
    Deque<Integer> deque = new LinkedList<>();
    for(int i =0;i<tokens.length;i++){
        //四则运算 加减乘除
        //加法
        if(tokens[i].equals("+")){
            //两数相加后入栈
            deque.push(deque.pop()+deque.pop());
        }
        //减法
        else if(tokens[i].equals("-")){
            //两数相减
            deque.push(-deque.pop()+deque.pop());
        }
        //乘法
        else if(tokens[i].equals("*")){
            deque.push(deque.pop()*deque.pop());
        }
        //除法
        else if(tokens[i].equals("/")){
                int temp1 = deque.pop();
                int temp2 = deque.pop();
                deque.push(temp2 / temp1);
        }else{
            //除了这四个符号,剩下的就是数字,把数字转为整型
          deque.push(Integer.parseInt(tokens[i]));
        }
    }
    return deque.pop();
    }
}

练习完以上的题目,有很多的收获。

题目的难度是依次递增的,但花费的时间也是递增的。虽然第一个题是最简单的,但是存在的细节也很多。昨天归纳完了栈和队列的模拟与总结了方法,今天就是实战。

从做题的流程下来,在第一个题上反而花费了最多的时间,很多条件的判断是最开始是没想到的,到慢慢琢磨,细品。把第一个题的问题处理完后,再来做后面的题就越来越快了。

今日的三道题主要是用到了栈的原理,还要多加巩固API的使用和对栈结构的处理!

回廊一寸相思地

落月成孤倚~

Fighting!

  • 21
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值