• 【LeetCode】115.最小栈(辅助栈,java实现)


    题目

    链接

    image-20200710205332872

    解答

    这道题的思想很简单:“以空间换时间”,使用辅助栈是常见的做法。

    思路分析:

    在代码实现的时候有两种方式:

    1、辅助栈和数据栈同步

    特点:编码简单,不用考虑一些边界情况,就有一点不好:辅助栈可能会存一些“不必要”的元素。

    2、辅助栈和数据栈不同步

    特点:由“辅助栈和数据栈同步”的思想,我们知道,当数据栈进来的数越来越大的时候,我们要在辅助栈顶放置和当前辅助栈顶一样的元素,这样做有点“浪费”。基于这一点,我们做一些“优化”,但是在编码上就要注意一些边界条件。

    (1)辅助栈为空的时候,必须放入新进来的数;

    (2)新来的数小于或者等于辅助栈栈顶元素的时候,才放入,特别注意这里“等于”要考虑进去,因为出栈的时候,连续的、相等的并且是最小值的元素要同步出栈

    (3)出栈的时候,辅助栈的栈顶元素等于数据栈的栈顶元素,才出栈。

    总结一下:出栈时,最小值出栈才同步;入栈时,最小值入栈才同步

    对比:个人觉得“同步栈”的方式更好一些,因为思路清楚,因为所有操作都同步进行,所以调试代码、定位问题也简单。“不同步栈”,虽然减少了一些空间,但是在“出栈”、“入栈”的时候还要做判断,也有性能上的消耗。

    方法一:辅助栈和数据栈同步

    参考代码 1

    import java.util.Stack;
    
    public class MinStack {
    
        // 数据栈
        private Stack<Integer> data;
        // 辅助栈
        private Stack<Integer> helper;
    
        /**
         * initialize your data structure here.
         */
        public MinStack() {
            data = new Stack<>();
            helper = new Stack<>();
        }
    
        // 思路 1:数据栈和辅助栈在任何时候都同步
    
        public void push(int x) {
            // 数据栈和辅助栈一定会增加元素
            data.add(x);
            if (helper.isEmpty() || helper.peek() >= x) {
                helper.add(x);
            } else {
                helper.add(helper.peek());
            }
        }
    
        public void pop() {
            // 两个栈都得 pop
            if (!data.isEmpty()) {
                helper.pop();
                data.pop();
            }
        }
    
        public int top() {
            if(!data.isEmpty()){
                return data.peek();
            }
            throw new RuntimeException("栈中元素为空,此操作非法");
        }
    
        public int getMin() {
            if(!helper.isEmpty()){
                return helper.peek();
            }
            throw new RuntimeException("栈中元素为空,此操作非法");
        }
    }
    

    复杂度分析

    • 时间复杂度:O(1),“出栈”、“入栈”、“查看栈顶元素”的操作不论数据规模多大,都只是有限个步骤,因此时间复杂度是:O(1)O(1)。
    • 空间复杂度:O(N),这里 N 是读出的数据的个数。

    方法二:辅助栈和数据栈不同步

    • 借用一个辅助栈min_stack,用于存获取stack中最小值。
    • 算法流程:
      • push()方法: 每当push()新值进来时,如果 小于等于 min_stack栈顶值,则一起push()min_stack,即更新了栈顶最小值;
      • pop()方法: 判断将pop()出去的元素值是否是min_stack栈顶元素值(即最小值),如果是则将min_stack栈顶元素一起pop(),这样可以保证min_stack栈顶元素始终是stack中的最小值。
      • getMin()方法: 返回min_stack栈顶即可。
    • min_stack作用分析:
      • min_stack等价于遍历stack所有元素,把升序的数字都删除掉,留下一个从栈底到栈顶降序的栈。
      • 相当于给stack中的降序元素做了标记,每当pop()这些降序元素,min_stack会将相应的栈顶元素pop()出去,保证其栈顶元素始终是stack中的最小元素。

    155.gif

    参考代码 2

    import java.util.Stack;
    
    public class MinStack {
    
        // 数据栈
        private Stack<Integer> data;
        // 辅助栈
        private Stack<Integer> helper;
    
        /**
         * initialize your data structure here.
         */
        public MinStack() {
            data = new Stack<>();
            helper = new Stack<>();
        }
    
        // 思路 2:辅助栈和数据栈不同步
        // 关键 1:辅助栈的元素空的时候,必须放入新进来的数
        // 关键 2:新来的数小于或者等于辅助栈栈顶元素的时候,才放入(特别注意这里等于要考虑进去)
        // 关键 3:出栈的时候,辅助栈的栈顶元素等于数据栈的栈顶元素,才出栈,即"出栈保持同步"就可以了
    
        public void push(int x) {
            // 辅助栈在必要的时候才增加
            data.add(x);
            // 关键 1 和 关键 2
            if (helper.isEmpty() || helper.peek() >= x) {
                helper.add(x);
            }
        }
    
        public void pop() {
            // 关键 3:data 一定得 pop()
            if (!data.isEmpty()) {
                // 注意:声明成 int 类型,这里完成了自动拆箱,从 Integer 转成了 int,因此下面的比较可以使用 "==" 运算符
                // 参考资料:https://www.cnblogs.com/GuoYaxiang/p/6931264.html
                // 如果把 top 变量声明成 Integer 类型,下面的比较就得使用 equals 方法
                int top = data.pop();
                if(top == helper.peek()){
                    helper.pop();
                }
            }
        }
    
        public int top() {
            if(!data.isEmpty()){
                return data.peek();
            }
            throw new RuntimeException("栈中元素为空,此操作非法");
        }
    
        public int getMin() {
            if(!helper.isEmpty()){
                return helper.peek();
            }
            throw new RuntimeException("栈中元素为空,此操作非法");
        }
    
    }
    

    参考代码3:

    class MinStack {
        private Stack<Integer> stack;
        private Stack<Integer> min_stack;
        public MinStack() {
            stack = new Stack<>();
            min_stack = new Stack<>();
        }
        public void push(int x) {
            stack.push(x);
            if(min_stack.isEmpty() || x <= min_stack.peek())
                min_stack.push(x);
        }
        public void pop() {
            if(stack.pop().equals(min_stack.peek()))
                min_stack.pop();
        }
        public int top() {
            return stack.peek();
        }
        public int getMin() {
            return min_stack.peek();
        }
    }
    

    复杂度分析

    • 时间复杂度:O(1),“出栈”、“入栈”、“查看栈顶元素”的操作不论数据规模多大,都只有有限个步骤,因此时间复杂度是:O(1)。
    • 空间复杂度:O(N),这里 N是读出的数据的个数。
  • 相关阅读:
    还原大备份mysql文件失败的解决方法
    第一次招聘
    Data Transformation Services (DTS) Package Example
    .net Page 执行事件顺序
    批量修改相同結構的table layout
    js 中exec、test、match、search、replace、split用法
    存储过程分页,按多条件排序(原创)
    Lisp 的本质 [转]
    【原创】jQuery方法的写法不同导致的兼容性
    WinForm 图片变灰方法
  • 原文地址:https://www.cnblogs.com/hzcya1995/p/13307974.html
Copyright © 2020-2023  润新知