Leetcode刷题记录-栈

Leetcode 1021 删除最外层的括号

删除最外层的括号

有效括号字符串为空 ("")、"(" + A + “)” 或 A + B,其中 A 和 B 都是有效的括号字符串,+ 代表字符串的连接。例如,"","()","(())()" 和 “(()(()))” 都是有效的括号字符串。
如果有效字符串 S 非空,且不存在将其拆分为 S = A+B 的方法,我们称其为原语(primitive),其中 A 和 B 都是非空有效括号字符串。
给出一个非空有效字符串 S,考虑将其进行原语化分解,使得:S = P_1 + P_2 + … + P_k,其中 P_i 是有效括号字符串原语。
对 S 进行原语化分解,删除分解中每个原语字符串的最外层括号,返回 S 。

示例一:

输入:"( ( ) ( ) ) ( ( ) )"
输出:"( )( )( )"
解释
输入字符串为 “( ( ) ( ) ) (( ) )”,原语化分解得到 “( ( ) ( ) )” + “( ( ) )”,
删除每个部分中的最外层括号后得到 “( )( )” + “( )” = “( ) ( )( )”。

示例二:

输入:"( ( ) ( ) ) ( ( ) ) ( ( ) ( ( ) ) )"
输出:"( ) ( ) ( ) ( ) ( ( ) )"
解释:
输入字符串为 “( ( ) ( ) ) ( ( ) ) ( ( ) ( ( ) ) )”,原语化分解得到 “( ( ) () )” + “( ( ) )” + “( ( ) ( ( ) ) )”,
删除每个部分中的最外层括号后得到 “( ) ()” + “( )” + “( ) ( ( ) )” = “( ) ( ) ( ) ( ) ( ( ) )”。

提示:

  • S.length <= 10000
  • S[i] 为 “(” 或 “)”
  • S 是一个有效括号字符串

解法一:暴力解法:拆解原语后删外层括号

思路:

  • 1.定义容器存储原语子串 new ArrayList();
  • 2.定义左括号、右括号计数器:int left = 0, right = 0;
  • 3.遍历字符串,读取到括号时对应计数器自增
  • 4.检查是否到达原语结尾,截取原语子串并添加到容器中
  • 5.遍历容器,删除最外层括号后合并成新串
public String removeOuterParentheses(String S){
       int len = S.length();
       //定义容器存储原语字串
       List<String> list = new ArrayList<>();
       //定义左括号、右括号计数器
       int right = 0,left = 0,lastOpr = 0;
       //遍历字符串,读取到括号时对应计数器自增
       for(int i = 0;i < len; ++i){
          char c = S.charAt(i);
          if(c == '('){
             left++;
          }else if(c ==')'){
             right++;
          }
       //检查是否到达某个原语结尾,截取原语子串添加到容器
          if(left ==right){
             list.add(S.substring(lastOpr, i + 1));
             lastOpr = i + 1;
            }     
       }
//public String substring(int beginIndex, int endIndex)
//返回一个新字符串,它是此字符串的一个子字符串。
//该子字符串从指定的 beginIndex 处开始,直到索引 endIndex - 1 处的字符。
//所以此处i+1
      //遍历容器中的原语子串,删除最外层括号后合并成新串
      StringBuilder result = new StringBuilder();
      for(String s : list){
         result.append(s.substring(1,s.length()-1));
        }
      return result.toString();
}

解法二:优化解法:直接定位原语内层子串

public String removeOuterParentheses(String S){
       int len = S.length();
       //定义容器存储删除外层括号后的原语子串
       StringBuilder result = new StringBuilder();
       //定义左括号、右括号计数器
       int left = 0,right = 0,lastOpr = 0;
       //遍历字符串,读取到括号时对应计数器自增
       for(int i = 0;i < len;++i){
            char ch = S.charAt(i);
            if(ch =='('){
               left++;
               }else if(ch == ')'){
                  right++;
                  }
            if(left == right){
               result.append(S.substring(++lastOpr,i));
               lastOpr=i+1;
             }
         }
     return result.toString();
}

解法三:栈解法

思路:

  • 遍历字符串,根据情况进行入栈/出栈操作栈实现代码: 读取到左括号,左括号入栈;读取到右括号,左括号出栈
  • 判断栈是否为空,若为空,找到了一个完整的原语
  • 截取不含最外层括号的原语子串并进行拼接
public String removeOuterParentheses(String S) {
       StringBuilder result = new StringBuilder();
       Stack stack = new Stack();
       int lastOpr = 0;
       //遍历字符串,根据情况进行出/入栈操作
       for(int i = 0;i < S.length();++i){
           char ch = S.charAt(i);
           if(ch == '('){
              stack.push(ch);
             }else if(ch == ')'){
              stack.pop();
              }
             //判断栈是否为空,若为空,则找到了一个完整的原语
            if(stack.isEmpty()){
               result.append(S.substring(++lastOpr,i));
               lastOpr = i + 1;
               }
           }
       return result.toString();
}

解法四:栈解法优化

思路:

  • 直接用数组取代栈:创建数组、栈顶索引,使用数组操作入栈和出栈
  • 将原字符串转为数组进行遍历char[] s = S.toCharArray();,避免多次调用charAt()方法
  • 去掉截取子串的操作,将原语字符直接拼接
  1. 读取到左括号:此前有数据,当前必属原语
  2. 读取到右括号:匹配后不为0,当前必属原语
public String removeOuterParentheses(String S) {
       StringBuilder result = new StringBuilder();
       //直接用数组代替栈
       int index = -1;
       int len = S.length();
       char[] stack = new char[len];
       char[] s = S.toCharArray();
       //遍历字符数组,根据情况进行出/入栈操作
       for(int i = 0;i < len;++i){
           char ch = s[i];
           if(ch=='('){//去掉截取子串的操作,将原语字符直接拼接
             if(index > -1){
                result.append(ch);
               }
              stack[++index] = ch;
            }else{//ch==')'
                stack[index--] = '\u0000';//栈顶的左括号出栈
               if(index >-1){
                  result.append(ch);
                  }   
           }
        }
        return result.toString();
}

Leetcode 155 最小栈

最小栈

设计一个支持pushpoptop操作,并能在常数时间内检索到最小元素的栈。

  • push(x):将元素 x 推入栈中。
  • pop():删除栈顶的元素
  • top():获取栈顶元素
  • 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

解法一:辅助栈

思路
可以用两个栈,一个栈去保存正常的入栈出栈的值,另一个栈去存最小值,也就是用栈顶保存当前所有元素的最小值。存最小值的栈的具体操作流程如下:

  • 将第一个元素入栈。
  • 新加入的元素如果大于栈顶元素,那么新加入的元素就不处理。
  • 新加入的元素如果小于等于栈顶元素,那么就将新元素入栈。
  • 出栈元素不等于栈顶元素,不操作。
  • 出栈元素等于栈顶元素,那么就将栈顶元素出栈。
class MinStack {
    private Stack<Integer> stack;
    private Stack<Integer> minStack;

    public MinStack() {
        stack = new Stack<>();
        minStack = new Stack<>();
    }
public void push(int x) {
     stack.push(x);
     if (!minStack.isEmpty()) {
        int top = minStack.peek();
        //小于的时候才入栈
        if (x <= top) {
           minStack.push(x);
            }
        }else{
            minStack.push(x);
        }
    }
public void pop() {
        int pop = stack.pop();
        int top = minStack.peek();
        //等于的时候再出栈
        if (pop == top) {
            minStack.pop();
        }
    }
public int top() {
        return stack.peek();
    }
public int getMin() {
        return minStack.peek();
    }
}

解法二:不借助栈

思路

  • 解法一中单独用了一个栈去保存所有最小值,那现在只用一个变量去保存最小值
    在这里插入图片描述
    如果把min 更新为 2,那么之前的最小值 3 就丢失了。如何把 3保存起来呢?把它在 2 之前压入栈中即可。
    在这里插入图片描述
    上边的最后一个状态,当出栈元素是最小元素时,我们只需要把 2 出栈,然后再出栈一次,把 3赋值给 min 即可。
  • 通过上边的方式,我们就只需要一个栈了。当有更小的值来的时候,我们只需要把之前的最小值入栈,当前更小的值再入栈即可。当这个最小值要出栈的时候,下一个值便是之前的最小值了。
class MinStack{
      int min = Integer.MAX_VALUE;
      Stack<Integer> stack = new Stack<Integer>();
      public void push(int x){
          //当前值更小
          if(x <= min){
          //将之前的更小值保存
            stack.push(min);
          //更新最小值
            min = x;
           }
          stack.push(x);
           }
     public void pop(){
          //如果弹出的值是最小值,那么将下一个元素更新为最小值
          if(stack.pop() == min) {
            min=stack.pop();
            }
        }
     public int top(){
        return stack.peek();
        }
     public int getMin(){
         return min;
}
}

Leetcode 946 验证栈序列

验证栈序列

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列 {1,2,3,4,5} 是某栈的压栈序列,序列 {4,5,3,2,1} 是该压栈序列对应的一个弹出序列,但 {4,3,5,1,2} 就不可能是该压栈序列的弹出序列。

解法一:辅助栈

思路
考虑借用一个辅助栈stack ,模拟 压入 / 弹出操作的排列。根据是否模拟成功,即可得到结果。

  • 入栈操作: 按照压栈序列的顺序执行。
  • 出栈操作: 每次入栈后,循环判断 “栈顶元素 == 弹出序列的当前元素” 是否成立,将符合弹出序列顺序的栈顶元素全部弹出。
public boolean validateStackSequences(int[] pushed,int[] poped){
     Stack<Integer> stack = new Stack<>();
     int i = 0;
     for(int num : pushed){
         stack.push(num);
         while(!stack.isEmpty() && stack.peek() == poped[i]) { // 循环判断与出栈
            stack.pop();
            i++;
            }
      }
  return stack.isEmpty();
}

解法二:不借助栈

O(1) 空间 O(n) 时间

public boolean validateStackSequences(int[] pushed, int[] popped) {
     int i = 0, j = 0;
     for (int e : pushed) {
         pushed[i] = e;
         while (i >= 0 && pushed[i] == popped[j]) {
             j++;
             i--;
           }
         i++;
      }
   return i == 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值