• 栈实现getMin


    题目

    实现一个特殊的栈,在实现栈的基本功能的基础上,在实现返回栈中最小元素的操作。

    要求

    1. pop、push、getMin操作的时间复杂度都是O(1).
    2. 设计的栈类型可以使用现成的栈结构。

    解答

    在设计上我们使用两个栈,一个栈用来保存当前栈中的元素,其功能和一个正常的栈没有区别,这个栈记为stackData;另一个栈用于保存每一步的最小值,这个栈记为stackMin。具体实现的方式有两种。

    第一种

    压入数据规则

    假设当前数据为newNum,现将其压入stackData。然后判断stackMin是否为空:

    • 如果为空,则newNum也压入stackMin。
    • 如果不为空,则比较newNum和stackMin的栈顶元素中哪一个更小:
    • 如果newNum更小或两者相等,则newNum也压入stackMin;
    • 如果stackMin中的栈顶元素小,则stackMin不压如任何内容。

    举例:一次压入3、4、5、1、2、1的过程中,stackData和stackMin的变化下图:

    数据弹出规则

      先在stackData中弹出栈顶元素,记为value。然后比较当前stackMin的栈顶元素和value哪一个更小。
      通过上文提到的压入规则可知,stackMin中存在的元素是从栈底到栈顶逐渐变小的,stackMin栈顶的元素既是stackMin栈的最小值,也是当前stackData栈中的最小值。所以不会出现value比stackMin的栈顶元素更小的情况,value只可能大于或等于stackMin的栈顶的元素。
      当value等于stackMin的栈顶元素时,stackMin弹出栈顶元素;当value大于stackMin的栈顶元素时,stackMin不弹出栈顶元素;返回false。
      很显然可以看出,压入与弹出规则是对应的。

    查询当前栈中的最小值操作

      由上文的压入数据规则和弹出数据规则可知,stackMin始终记录着stackData中的最小值,所以,stackMin栈顶元素始终是当前stackData中的最小值。

    下面看一下代码:

    public class MyStack1 {
    
        private Stack<Integer> stackData;
        private Stack<Integer> stackMin;
    
        public MyStack1(){
            stackData = new Stack<>();
            stackMin = new Stack<>();
        }
    
        public void push(int newNum){
            if(this.stackMin.isEmpty()){
                this.stackMin.push(newNum);
            }else if(newNum<=this.getMin()){
                this.stackMin.push(newNum);
            }
            this.stackData.push(newNum);
        }
    
        public int pop(){
            if(this.stackData.isEmpty()){
                throw new RuntimeException("Your stack is empty.");
            }
            int value = this.stackData.pop();
            if(value==this.getMin()){
                this.stackMin.pop();
            }
            return value;
        }
    
        public int getMin(){
            if(this.stackMin.isEmpty()){
                throw new RuntimeException("Your stack is empty");
            }
            return this.stackMin.peek();
        }
    }
    

    第二种

    压入数据的规则

    假设当前数据为newNum,现将其压入stackData、然后判断stackMin是否为空。

    如果为空,则newNum也压入stackMin;如果不为空,则比较newNum和stackMin的栈顶元素中哪一个更小;

    如果newNum更小或者两者相等,则newNum也压入stackMin;如果stackMin中栈顶元素小,则吧stackMin的栈顶元素重复压入stackMin,即在栈顶元素上再压入一个栈顶元素。

    举例:一次压入3、4、5、1、2、1的过程,stackData和stackMin的变化如下图。

    弹出数据规则

    在stackData中弹出数据,弹出数据记为value;弹出stackMin中的栈顶;返回value。很明显可以看出,压入与弹出规则是对应的。

    查询当前栈中的最小值操作

    由上文的压入数据规则和弹出数据规则可知,stackMin始终记录着stackData中的最小值,所以stackMin的栈顶元素始终是当前stackData中的最小值。

    方案二的代码实现如MyStack2类所示:

    public class MyStack2 {
        private Stack<Integer> stackData;
        private Stack<Integer> stackMin;
    
        public MyStack2(){
            stackData = new Stack<>();
            stackMin = new Stack<>();
        }
    
        public void push(int newNum){
            if(this.stackMin.isEmpty()){
                this.stackMin.push(newNum);
            }else if(newNum<this.getMin()){
                this.stackMin.push(newNum);
            }else{
                int newMin = this.stackMin.peek();
                this.stackMin.push(newMin);
            }
            this.stackData.push(newNum);
        }
    
        public int pop(){
            if(this.stackData.isEmpty()){
                throw new RuntimeException("Your stack is empty.");
            }
            this.stackMin.pop();
    
            return this.stackData.pop();
        }
    
        public int getMin(){
            if(this.stackMin.isEmpty()){
                throw new RuntimeException("Your stack is empty");
            }
            return this.stackMin.peek();
        }
    }
    

    方案一和方案二其实都是stackMin栈保存着stackData每一步的最小值。共同是所有操作的时间复杂度都为O(1)、空间复杂度都是O(n)。区别是:方案一种stackMin压入时稍省空间,但是弹出操作稍费时间;方案二中stackMin压入时稍费空间,但是弹出操作稍省时间。

  • 相关阅读:
    数组的操作方法
    数组遍历的方法以及区别
    组件内的守卫
    路由守卫
    软件工程
    java web (j2ee)学习路线 —— 将青春交给命运
    团队作业(一)- 第五组
    软件工程
    软件工程-第二次作业
    java局部/成员/静态/实例变量
  • 原文地址:https://www.cnblogs.com/luanpeng/p/6728483.html
Copyright © 2020-2023  润新知