zoukankan      html  css  js  c++  java
  • 栈:删除最外层的括号 (Leetcode 1021 / 155 / 1172 / 剑指31 / 面试 03.03)

     

     

     

     

     

     

     

     

     

    /**
    * 解法一:暴力解法思路分析
    * 1.定义容器存储原语子串
    * new ArrayList<String>();
    * 2.定义左括号、右括号计数器:
    * int left = 0, right = 0;
    * 3.遍历字符串,读取到括号时对应计数器自增
    * 4.检查是否到达原语结尾,截取原语子串并添加到容器中
    * 5.遍历容器,删除最外层括号后合并成新串
    *
    * 边界问题:
    * 遍历字符串,注意索引越界:i < S.length()
    * 截取原语字符串时,注意起止索引:[start, end)
    * 细节问题:
    * 需要记录上一次截取原语子串之后的位置
    * 删除原语子串的最外层括号,其实就是重新截取
    * @param S
    * @return
    */
    public String removeOuterParentheses(String S) {
      int len = S.length();
      // 1.定义容器存储原语子串
      List<String> list = new ArrayList<>();
      // 2.定义左括号、右括号计数器
      int left = 0, right = 0, lastOpr = 0;
      // 3.遍历字符串,读取到括号时对应计数器自增
      for (int i = 0; i < len; i++) {
        char c = S.charAt(i);
        if (c == '(') {
          left++;
        } else if (c == ')') {
          right++;
        }
        // 4.检查是否到达某个原语结尾,截取原语子串添加到容器
        if (left == right) {
          list.add(S.substring(lastOpr, i + 1));
          lastOpr = i + 1;
        }
      }
      // 5.遍历容器中的原语子串,删除最外层后合并成新串
      StringBuilder sb = new StringBuilder();
      for (String s : list) {
        sb.append(s.substring(1, s.length() - 1));
      }
      return sb.toString();
    }

     

    /**
    * 解法二:优化解法思路分析
    * 1.定义容器存储删除外层括号后的原语子串
    * new StringBuilder();
    * 2.定义左括号、右括号计数器:
    * int left = 0, right = 0;
    * 3.遍历字符串,读取到括号时对应计数器自增
    * 4.检查是否到达原语结尾,截取不包含最外层的原语子串并拼接到容器中
    *
    * @param S
    * @return
    */
    public String removeOuterParentheses(String S) {
      int len = S.length();
      // 1.定义容器存储删除外层括号后的原语子串
      StringBuilder sb = new StringBuilder();
      // 2.定义左括号、右括号计数器
      int left = 0, right = 0, lastOpr = 0;
      // 3.遍历字符串,读取到括号时对应计数器自增
      for (int i = 0; i < len; i++) {
        char c = S.charAt(i);
        if (c == '(') {
          left++;
        } else if (c == ')') {
         right++;
        }
        // 4.检查是否到达某个原语结尾,截取不包含最外层的原语子串添加到容器
        if (left == right) {
          sb.append(S.substring(++lastOpr, i));
          lastOpr = i + 1;
        }
      }
      return sb.toString();
    }

     

     

     

     

    /**
    * 栈:后进先出
    * @param <E>
    */
    class Stack<E> {
      Object[] elements = new Object[10000];
      int index = -1; // 栈顶索引
      int size = 0; // 栈中元素个数
      public Stack() {
      }
      /**
      * 往栈顶插入元素
      * @param c
      */
      public void push(E c) {
        elements[++index] = c;
        size++;
      }
      /**
      * 从栈顶获取数据,不移出
      栈解法优化,使用栈的思想,直接用数组取代栈:
      * @return
      */
      public E peek() {
        if (index < 0) {
          return null;
        }
        return (E)elements[index];
      }
      /**
      * 从栈顶移出元素
      * @return
      */
      public E pop() {
        E e = peek();
        if (e != null) {
          elements[index] = null; // 移出动作
          index--; // 栈顶下移
          size--;
        }
      return e;
    }
      public boolean isEmpty() {
        return size == 0;
      }
    }
    /**
    * 最优解:栈解法
    * 1.使用数组模拟一个栈,临时存储左括号字符
    * push(Character) ; pop(); isEmpty()
    * 2.遍历字符串,根据情况进行入栈/出栈操作
    栈实现代码:
    * 读取到左括号,左括号入栈
    * 读取到右括号,左括号出栈
    * 3.判断栈是否为空,若为空,找到了一个完整的原语
    * 4.截取不含最外层括号的原语子串并进行拼接
    *
    * @param S
    * @return
    */
    public String removeOuterParentheses(String S) {
      StringBuilder result = new StringBuilder();
      // 1.使用数组模拟一个栈,临时存储字符,替代计数器
      Stack stack = new Stack();
      int lastOpr = 0;
      // 2.遍历字符串,根据情况进行入栈 / 出栈操作
      for (int i = 0; i < S.length(); i++) {
        char ch = S.charAt(i);
        if (ch == '(') { // 遇到左括号,左括号入栈
          stack.push(ch);
        } else if (ch == ')') { // 遇到右括号,左括号出栈
          stack.pop(); // 栈顶的左括号出栈
        }
        // 3.判断栈是否为空,若为空,找到了一个完整的原语
        if (stack.isEmpty()) {
          // 4.截取不含最外层括号的原语子串并进行拼接
          result.append(S.substring(lastOpr + 1, i));// 去掉原语的最外层括号并追加到结果
          lastOpr = i + 1;// 往后找,再次初始化原语开始位置
        }
      }
      return result.toString();
    }

     

     

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

    最最优解

     

     

     

     

    Leetcode 155 -  最小栈

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

    • 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.

    提示:

      • pop、top 和 getMin 操作总是在 非空栈 上调用。
    //思路:使用两个队列实现最小栈
    class MinStack { 
        Deque<Integer> xStack; //主栈,存放所有入栈数据
        Deque<Integer> minStack; //每次入栈当前最小数据
        public MinStack() {
            xStack = new LinkedList<Integer>();
            minStack = new LinkedList<Integer>();
            minStack.push(Integer.MAX_VALUE);//默认最小数据为整形最大值
        }
    
        public void push(int x) {
            xStack.push(x);//入栈
            minStack.push( minStack.peek()< x? minStack.peek():x); //将当前最小值入栈,为保证数量一致,所以会重复存放最小值
        }
    
        public void pop() {
            xStack.pop();//出栈
            minStack.pop();//当前最小值也出栈
        }
    
        public int top() {
            return xStack.peek();//获取栈顶元素
        }
    
        public int getMin() {
            return minStack.peek();//通过最小栈获取当前最小元素
        }
    }
    
    /**
     * Your MinStack object will be instantiated and called as such:
     * MinStack obj = new MinStack();
     * obj.push(x);
     * obj.pop();
     * int param_3 = obj.top();
     * int param_4 = obj.getMin();
     */

    剑指Offer 31 -  栈的压入、弹出序列

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

    示例 1:

      输入:pushed = [1,2,3,4,5], popped = [4,5,3,2,1]
      输出:true
      解释:我们可以按以下顺序执行:
      push(1), push(2), push(3), push(4), pop() -> 4,
      push(5), pop() -> 5, pop() -> 3, pop() -> 2, pop() -> 1
    示例 2:

      输入:pushed = [1,2,3,4,5], popped = [4,3,5,1,2]
      输出:false
      解释:1 不能在 2 之前弹出。

    提示:

      0 <= pushed.length == popped.length <= 1000
      0 <= pushed[i], popped[i] < 1000
      pushed 是 popped 的排列。

    //思路:每次比较栈顶元素与弹出序列是否一致,一致则出栈
    class Solution {
        public boolean validateStackSequences(int[] pushed, int[] popped) {
           Stack<Integer> stack = new Stack<>();
            int i = 0;
            for(int num : pushed) {
                stack.push(num); // num 入栈
                while(!stack.isEmpty() && stack.peek() == popped[i]) { // 循环判断与出栈
                    stack.pop();
                    i++;
                }
            }
            return stack.isEmpty();
        }
    }

    堆盘子 - 面试 03.03

    堆盘子。设想有一堆盘子,堆太高可能会倒下来。

    因此,在现实生活中,盘子堆到一定高度时,我们就会另外堆一堆盘子。请实现数据结构SetOfStacks,模拟这种行为。

    SetOfStacks应该由多个栈组成,并且在前一个栈填满时新建一个栈。

    此外,SetOfStacks.push()和SetOfStacks.pop()应该与普通栈的操作方法相同(也就是说,pop()返回的值,应该跟只有一个栈时的情况一样)。

    进阶:实现一个popAt(int index)方法,根据指定的子栈,执行pop操作。

    当某个栈为空时,应当删除该栈。当栈中没有元素或不存在该栈时,pop,popAt 应返回 -1.

    示例1:

    输入:
      ["StackOfPlates", "push", "push", "popAt", "pop", "pop"]
      [[1], [1], [2], [1], [], []]
    输出:
      [null, null, null, 2, 1, -1]


    示例2:

    输入:
      ["StackOfPlates", "push", "push", "push", "popAt", "popAt", "popAt"]
      [[2], [1], [2], [3], [0], [0], [0]]
    输出:
      [null, null, null, null, 2, 1, 3]

    class StackOfPlates {
        private List<Stack<Integer>> stackList;
        private int cap;
        public StackOfPlates(int cap) {
            stackList = new ArrayList<>(); //使用ArrayList来存储所有的栈
            this.cap = cap;
        }
    
        public void push(int val) {
            if (cap <= 0) {//处理边界
                return;
            }
    
            if (stackList.isEmpty() || stackList.get(stackList.size() - 1).size() == cap) {//处理最后一个栈满的情况
                Stack<Integer> stack = new Stack<>();
                stack.push(val);
                stackList.add(stack);
                return;
            }
    
            stackList.get(stackList.size() - 1).push(val);
        }
    
        public int pop() {
            return popAt(stackList.size() - 1);
        }
    
        public int popAt(int index) {
            if (index < 0 || index >= stackList.size()) {
                return -1;
            }
    
            Stack<Integer> stack = stackList.get(index);
            if (stack.isEmpty()) {
                return -1;
            }
    
            int res = stack.pop();
    
            if (stack.isEmpty()) {
                stackList.remove(index);
            }
    
            return res;
        }
    }

    餐盘栈 - Leetcode 1172

    我们把无限数量 ∞ 的栈排成一行,按从左到右的次序从 0 开始编号。每个栈的的最大容量 capacity 都相同。

    实现一个叫「餐盘」的类 DinnerPlates:

    DinnerPlates(int capacity) - 给出栈的最大容量 capacity。
    void push(int val) - 将给出的正整数 val 推入 从左往右第一个 没有满的栈。
    int pop() - 返回 从右往左第一个 非空栈顶部的值,并将其从栈中删除;如果所有的栈都是空的,请返回 -1。
    int popAtStack(int index) - 返回编号 index 的栈顶部的值,并将其从栈中删除;如果编号 index 的栈是空的,请返回 -1。

    示例:

    输入:
      ["DinnerPlates","push","push","push","push","push","popAtStack","push","push","popAtStack","popAtStack","pop","pop","pop","pop","pop"]
      [[2],[1],[2],[3],[4],[5],[0],[20],[21],[0],[2],[],[],[],[],[]]
    输出:
      [null,null,null,null,null,null,2,null,null,20,21,5,4,3,1,-1]

    解释:
    DinnerPlates D = DinnerPlates(2); // 初始化,栈最大容量 capacity = 2
    D.push(1);
    D.push(2);
    D.push(3);
    D.push(4);
    D.push(5); // 栈的现状为:
    2 4
    1 3 5
    ﹈ ﹈ ﹈
    D.popAtStack(0); // 返回 2。栈的现状为:
    4
    1 3 5
    ﹈ ﹈ ﹈
    D.push(20); // 栈的现状为:
    20 4
    1 3 5
    ﹈ ﹈ ﹈
    D.push(21); // 栈的现状为:
    20 4 21
    1 3 5
    ﹈ ﹈ ﹈
    D.popAtStack(0); // 返回 20。栈的现状为:
    4 21
    1 3 5
    ﹈ ﹈ ﹈
    D.popAtStack(2); // 返回 21。栈的现状为:
    4
    1 3 5
    ﹈ ﹈ ﹈
    D.pop() // 返回 5。栈的现状为:
    4
    1 3
    ﹈ ﹈
    D.pop() // 返回 4。栈的现状为:
    1 3
    ﹈ ﹈
    D.pop() // 返回 3。栈的现状为:
    1

    D.pop() // 返回 1。现在没有栈。
    D.pop() // 返回 -1。仍然没有栈。

    //采用双指针思想,后面后讲到的
    class DinnerPlates {
        private int capacity = 0;
        private int left = 0;//从左边开始第一个不满的,严格控制
        private int right = 0;//从右边开始第一个非空的,宽松
        private List<Stack<Integer>> list = new ArrayList<>();
    
        public DinnerPlates(int capacity) {
            this.capacity = capacity;
            Stack<Integer> stack2 = new Stack();
            list.add(stack2);
        }
    
        /*
            直接在left所在位置进行入栈,push后需要遍历查找下一个未满栈
            细节问题:
                1. 需要对list进行扩容操作
                2. 需要更新right位置(right宽松,不保证一定是非空,可以在第一个非空的右边)
         */
        public void push(int val) {
            if (left < 0)
                left = 0;
            Stack<Integer> stack = list.get(left);
            stack.push(val);
            //达到容量,则找后面的第一个未满栈
            while (left < list.size() && list.get(left).size() == capacity) {
                left++;
            }
    
            if (left >= list.size()) { //需要增加栈的数量
                Stack<Integer> stack2 = new Stack();
                list.add(stack2);
            }
    
            if (right < left) { //新的栈在right以右,更新right的位置
                right = left;
            }
        }
    
        /*
            以right为起点,往左遍历查找第一个非空栈
            细节问题:
                移除数据后,需要判断所在位置是不是比left更小,如果满足则需要更新left
                移除数据后,需要判断所在位置是不是空栈,空栈则将right左移一个(right宽松,不保证一定是非空,可以在第一个非空的右边)
         */
        public int pop() {
            for (int i = right; i >= 0; i--) {//从right开始找第一个非空栈,乐观情况right就是想要的位置
                Stack<Integer> stack = list.get(i);
                if (!stack.isEmpty()) {
                    right = i;
                    Integer val = stack.pop();
                    if (left > right) { //如果移除的位置在left左边,需要将left修复到right位置
                        left = right;
                    }
                    if (stack.isEmpty()) {//pop之后,right需要再向左移动一个(这里不保证right的新位置一定为非空栈)
                        right--;
                    }
                    return val;
                }
            }
            return -1;
        }
    
        /*
          移除数据后,需要判断所在位置是不是比left更小,如果满足则需要更新left
         */
        public int popAtStack(int index) {
            if (index > list.size() - 1) //越界检查
                return -1;
            Stack<Integer> stack = list.get(index);
            if (!stack.isEmpty()) { //栈不为空
                if (left > index) { //在左边界之前,更新左边界执行更靠左的未满栈
                    left = index;
                }
                return stack.pop();
            } else {
                return -1;
            }
        }
    }
    /**
     * Your DinnerPlates object will be instantiated and called as such:
     * DinnerPlates obj = new DinnerPlates(capacity);
     * obj.push(val);
     * int param_2 = obj.pop();
     * int param_3 = obj.popAtStack(index);
     */
  • 相关阅读:
    hibernate对应的annocation版本
    Struts 2 OGNL
    Struts2的Stack Context和ValueStack
    Struts2中的OGNL详解
    struts2中根对象以及ognl .
    在Struts 2中实现IoC
    Struts2的属性驱动与模型驱动的区别
    Struts2的模型驱动
    Java中线程的锁和数据库中的事务隔离级别
    为什么socket编程要用到多线程
  • 原文地址:https://www.cnblogs.com/JasperZhao/p/15039966.html
Copyright © 2011-2022 走看看