栈的经典算法问题-算法通关村

栈的经典算法问题-算法通关村


1.括号匹配问题

  • LeetCode20:给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串s,判断字符串是否有效。有效字符串需满足:

    1. 左括号必须用相同类型的右括号闭合。
    2. 左括号必须以正确的顺序闭合。
  • 示例1:
    输入: s = “() [] {}”
    输出:true
  • 本题比较麻烦的是如何判断两个符号是不是一组的,我们可以用哈希表将所有符号先存储,左半边做key,右半边做value。遍历字符串的时候,遇到左半边符号就入栈,遇到右半边符号就与栈顶的符号比较,不匹配就返回false。

  •   public static boolean isValid(String str){
              if(str.length() <= 1){
                  return false;
              }
              //将左括号和右括号的对应关系存储在其中
              Map<Character, Character> smap = new HashMap();
              smap.put('(', ')');
              smap.put('{', '}');
              smap.put('[', ']');
              //存储读取到的左括号
              Stack<Character> stack = new Stack();
              for(int i = 0; i < str.length(); i++){
                  char item = str.charAt(i);
                  if(smap.containsKey(item)){
                      stack.push(item);
                  }else{
                      if(!stack.isEmpty()){
                          Character left = stack.pop();
                          char rightChar = smap.get(left);
                          if(rightChar != item){
                              return false;
                          }
                      }else{
                          return false;
                      }
                  }
              }
          //循环结束后,若栈空则stak是括号有效的字符串,否则说明还有左括号没匹配
              return stack.isEmpty();
          }
    
  • LeetCode给我们造了十几个括号匹配的问题,都是条件变来变去,但是解决起来有难有易,如果你感兴趣,可以继续研究一下:Leetcode20 有效的括号、LeetCode22.括号生成、LeetCode32.最长有效括号、LeetCode301.删除无效的括号和leetcode 856 括号的分数等。


2最小栈

  • LeetCode 155:设计一个支持 push,pop,top 操作,并能在常数时间内检索到最小元素的栈。

  • 实现MinStack类:

  • MinStack() 初始化堆栈对象
    void push(int val) 将元素val推入堆栈
    void pop() 删除堆栈顶部的元素
    int top() 获取堆栈顶部的元素
    int getMin() 获取堆栈中的最小元素
  • 示例:

  • 输入:
    [“MinStack”,“push”,“push”,“push”,“getMin”,“pop”,“top”,“getMin”]
    [[],[-2],[0],[-3],[],[],[],[]]
    输出:
    [null,null,null,null,-3,null,0,-2]
    解释:
    MinStack minStack = new MinStack();
    minStack.push(-2);
    minStack.push(0);
    minStack.push(-3);
    minStack.getMin(); --> 返回 -3.
    minStack.pop();
    minStack.top(); --> 返回 0.
    minStack.getMin(); --> 返回 -2.
  • 题目要求在常数时间内获得栈中的最小值,因此不能在 getMin()的时候再去计算最小值,最好应该在 push 或者 pop的时候就已经计算好了当前栈中的最小值。
    对于栈来说,如果一个元素 a 在入栈时,栈里有其它的元素b,c,d,那么无论这个栈在之后经历了什么操作,只要 a 在栈中,b, c, d 就一定在栈中,因为在 a 被弹出之前,b, c, d 不会被弹
    出。

  • 因此,在操作过程中的任意一个时刻,只要栈顶的元素是a,那么我们就可以确定栈里面现在的元素一定是 a, b,C,d。那么,我们可以在每个元素a入栈时把当前栈的最小值m 存储起来。在这之后无论何时,如果栈顶元素是 a,我们就可以直接返回存储的最小值m。

  • 按照上面的思路,我们只需要设计一个数据结构,使得每个元素 a 与其相应的最小值m 时刻保持一一对应。因此我们可以使用一个辅助栈,与元素栈同步插入与删除,用于存储与每个元素对应的最小值。

    • 当一个元素要入栈时,我们取当前辅助栈的栈顶存储的最小值,与当前元素比较得出最小值,将这个最小值插入辅助栈中;
    • 当一个元素要出栈时,我们把辅助栈的栈顶元素也一并弹出;
      在任意一个时刻,栈内元素的最小值就存储在辅助栈的栈顶元素中。
  •   class MinStack {
          Deque<Integer> xStack;
          Deque<Integer> minStack;
          public MinStack(){
              xStack = new ArrayDeque<Integer>();
              minStack = new ArrayDeque<Integer>();
              //Integer.MAX_VALUE是一个Java中整数类型Integer的最大值。
              // 它的值是2^31 - 1,即2147483647。
              //以便后续的push方法能够正确更新最小值
              minStack.push(Integer.MAX_VALUE);
          }
          public void push(int x){
              xStack.push(x);
              //比较栈中最小值和待压入元素x的大小,并将较小的值压入minStack栈中。
              minStack.push(Math.min(minStack.peek(), x));
          }
          public void pop(){
              xStack.pop();
              minStack.pop();
          }
          public int top(){
              return xStack.peek();
          }
          public int getMin(){
              return minStack.peek();
          }
      }
    
  • 不知道有没有小伙伴和我有一样的疑问,创建栈为什么不用 Stack,而是用 Duque(Java 推荐使用)

  • Deque是Java中的双端队列(Double Ended Queue)接口,它可以在队列的两端进行插入和删除操作。

    Deque接口继承自Queue接口,它定义了许多用于操作双端队列的方法,包括插入、删除、查找和访问元素等操作。常用的方法包括:

    • addFirst(element):在双端队列的头部插入元素。
    • addLast(element):在双端队列的尾部插入元素。
    • removeFirst():删除并返回双端队列的头部元素。
    • removeLast():删除并返回双端队列的尾部元素。
    • getFirst():返回双端队列的头部元素,但不从队列中删除。
    • getLast():返回双端队列的尾部元素,但不从队列中删除。
    • size():返回双端队列中的元素个数。

    Deque接口的常见实现类包括:

    • LinkedList:使用链表实现的双端队列,可以作为栈和队列来使用。

    •   Deque<Integer> xStack;
        Deque<Integer> minStack;
        public MinStack(){
           xStack = new LinkedList<>();
           minStack = new LinkedList<>();
        }    
      
    • ArrayDeque:使用动态数组实现的双端队列,效率比LinkedList略高。

    使用Deque接口,可以灵活地**实现先进先出队列(FIFO)、后进先出栈(LIFO)**以及双向队列的功能,具体实现可以根据需求选择合适的实现类。


3最大栈

  • LeetCode716: 设计一个最大栈数据结构,即支持栈操作,又支持查找栈中最大元素。

  • 实现MaxStack类:

  • MaxStack() 初始化栈对象
    void push(int x) 将元素 x 压入栈中。
    int pop() 移除栈顶元素并返回这个元素。
    int top() 返回栈顶元素,无需移除。
    int peekMax() 检索并返回栈中最大元素,无需移除。
    int popMax() 检索并返回栈中最大元素,并将其移除。如果有多个最大元素,只要移除 最靠近栈顶 的那个。
  • 示例:

  • 输入:
    [“MaxStack”, “push”, “push”, “push”, “top”, “popMax”, “top”, “peekMax”, “pop”, “top”]
    [[], [5], [1], [5], [], [], [], [], [], []]
    输出:
    [null, null, null, null, 5, 5, 1, 5, 1, 5]
    解释:
    MaxStack stk = new MaxStack();
    stk.push(5); // [5] - 5 既是栈顶元素,也是最大元素
    stk.push(1); // [5, 1] - 栈顶元素是 1,最大元素是 5
    stk.push(5); // [5, 1, 5] - 5 既是栈顶元素,也是最大元素
    stk.top(); // 返回 5,[5, 1, 5] - 栈没有改变
    stk.popMax(); // 返回 5,[5, 1] - 栈发生改变,栈顶元素不再是最大元素
    stk.top(); // 返回 1,[5, 1] - 栈没有改变
    stk.peekMax(); // 返回 5,[5, 1] - 栈没有改变
    stk.pop(); // 返回 1,[5] - 此操作后,5 既是栈顶元素,也是最大元素
    stk.top(); // 返回 5,[5] - 栈没有改变
  •   class MaxStack {
          Deque<Integer> xStack;
          Deque<Integer> maxStack;
          public MaxStack(){
              xStack = new ArrayDeque<Integer>();
              maxStack = new ArrayDeque<Integer>();
              //以便后续的push方法能够正确更新最大值
              maxStack.push(Integer.MIN_VALUE);
          }
          public void push(int x){
              xStack.push(x);
              //比较栈中最小值和待压入元素x的大小,并将较大的值压入minStack栈中。
              maxStack.push(Math.max(maxStack.peek(), x));
          }
          public int pop(){
              maxStack.pop();
              return xStack.pop();
          }
          public int top(){
              return xStack.peek();
          }
          public int popMax(){
              int max = peekMax();
              Deque<Integer> ds = new ArrayDeque();
              while(top() != max){
                  ds.push(pop());
              }
              pop();
              while(!ds.isEmpty()){
                  push(ds.pop());
              }
              return max;
          }
          public int peekMax(){
              return maxStack.peek();
          }
      }
      
      ``
      
    
  • 8
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

种一棵树leng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值