设计一个具有获取最小值(getMin)方法的栈

一、题目描述

实现一个特殊的栈结构,该栈在具有一般栈的结构的基础上还具有获取栈最小元素的功能。

二、题目要求

1.栈的pop、push、getMin操作的时间复杂度均为O(1);

2.设计特殊栈的时候可以使用现成的栈作为工具去完成。

三、题目解析

题目要求提醒我们可以使用现成的栈的数据结构,其实也就是告诉我们要使用其作为工具去构建我们的特殊的栈,因为其getMin操作是O(1),因此告诉我们其操作肯定不是在自己的栈里瞎折腾,因此我们需要两个栈,一个用来存储基本数据,另一个用来存储getMin操作得到的数字。此时假设我们存储数据的栈的名字为stackData,存储getMin数字的栈为stackMin。

此处提供的实现方式有两种,如下。

第一种方案:

对于stackData栈没有什么可以说的,就是对应特殊栈的pop、push使用一个普通栈去存储就可以了,问题的关键主要是子在push和pop的时候对于stackMin的操作。假设当前数据为newData,我们执行push操作时,先将其压入stackData种,然后判断stackMin是否为null,如果为null,则将其压入stackMin里,如果不为null,则比较newData和stackMin的栈顶元素谁小,如果newData小或者相等就将其压入stackMin中,否则则不压入;当执行pop方法时,stackData正常弹出栈顶元素,根据上面描述的push规则,stackMin的元素大小其实是按照从栈底到栈顶从小到大排列的,且其元素的个数是要小于等于stackData的,因此在执行pop操作时,需要比较要弹出的元素(记为data1)和栈顶元素大小进行比较,若data1比栈顶元素大,则直接返回stackMin的栈顶元素的值,并不弹出,否则则弹出;执行getMin操作就非常简单了,就直接返回stackMin栈顶元素即可。

第二种方案:

其实第二种方案的思想和第一种并没有什么不同,通过观察第一种方案的操作,其每次pop都要进行比较,此种方案就是构造一个可以直接pop的stackMin,其实就是在当newValue压入的时候,当newData的大小大于stackMin的栈顶元素时,同步压入一个stackMin的栈顶元素,这样就可以保证stackMin的元素数目和stackData保持一致,这样在pop时直接调用其原生的pop方法即可完成功能。

两种方案的比较:

其相同点为时间复杂度和空间复杂度都为O(1)和O(n),不同点是第一种在push的时候稍省空间但pop操作稍费时间,第二种在push的时候稍费空间,但是pop的时候稍省时间。

四、代码参考

import java.util.Stack;

public class MyStack1 {
    /**设置一个具有getMin功能的栈
     * 设置一个特殊的栈,在完成栈的基本功能的时候 然后再实现返回栈的最小元素的操作
     * 要求:
     * pop push getMin的操作都是O(1)的 设计栈的时候可以使用现成的栈结构
     * --
     * 在设计的时候 我们考虑设计两个栈 一个是正常的栈 其操作也正常记为stackData 另外一个栈用来记录每一步的最小值这个栈记为stackMin,实现方式有两种:
     * */
    /*
    * 第一种设计方案:
     * 1.压入规则
     * 首先一个value来的时候 首先判断stackMin是否为null 若stackMin为null 则把value直接压入stackMin
     * 当stackMin不为null的时候 value与stackMin的栈顶元素进行比较 若value比栈顶元素小或相等 则把value压入栈 如果value比栈顶元素大 则不压入任何值
     * 2.弹出规则
     * 弹出规则和压入规则相对应 我们可以知道 stackMin的栈顶元素就是当前stackData的最小值 所以当value弹出的时候和stackMin的栈顶元素进行比较
     * 相等时 弹出stackMin的栈顶元素 当当前value大于stackMin的栈顶元素时 不弹出栈顶元素 直接返回value的值
    * */
    private Stack stackData;
    private Stack stackMin;
    public MyStack1() {
        this.stackData = new Stack();
        this.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();
    }
}
class MysStack2 {
    /**
     * 第二种方案:
     * 其实第二种方案的不同的地方就是在压入的时候 当value的值比栈顶元素大的时候 压入一个和原来栈顶元素一样的值
     * 当弹出的时候 弹出一个值stackMin栈就从弹出一个栈顶元素
     * 比较:第一种方案在压入数据的时候比较省空间 第二中方案在弹出的时候比较省时间
     * 但是其时间复杂度O(1)和空间复杂度都是O(n)
     * */
    private Stack stackData;
    private Stack stackMin;

    public void push(int newValue) {
        if(this.stackMin.isEmpty()) {
            this.stackMin.push(newValue);
        } else if(newValue < this.getmin()) {
            this.stackMin.push(newValue);
        } else {
            int newMin = this.stackMin.peek();
            this.stackMin.push(newMin);
        }
    }

    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();
    }

 

你可能感兴趣的:(算法,数据结构)