设计一个有getMin功能的栈

命题

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

要求:

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

难度 ♥(压力好大...)

设计思路:

两个栈-普通栈+getMin栈,它的主要目的还是要获取到stack对象的最小值,如果两个栈同为最小值也一并被pop()方法弹出(实现弹栈的基本功能罢了)

方案一

让较大的数不压入最小值的栈中,stackMin不做任何操作继续保持原样,这样即可通过栈对象的peek()方法返回最小值

这里写图片描述

具体实现

package com.demo.mystack;

import java.util.Stack;
/**
 * 实现一个特殊的栈,在实现栈的基本功能的基础上,再实现返回栈中最小元素的操作
 * 要求:
 * 1、pop、push、getMin操作的时间复杂度都是O(1)
 * 2、设计的栈类型可以使用现成的栈结构
 */

/**
 * @author lorem
 * @date 2018/8/19
 */
public class MyStack {
    /**
     * stackData 基本信息栈
     * stackMin 最小栈
     **/
    private Stack<Integer> stackData;
    private Stack<Integer> stackMin;
    
    @Test
    public void test(){
       MyStack myStack = new MyStack();
        myStack.push(2);
        myStack.push(5);
        myStack.push(4);
        myStack.push(1);
        System.out.println(myStack.pop());
        System.out.println(myStack.getmin());
        MyStack myStack1 = new MyStack();
        myStack1.pop();
    }
/**
* 通过构造方法,实例化两个Stack对象 stackData、stackMin
*/
    public MyStack(){
        this.stackData = new Stack<Integer>();
        this.stackMin = new Stack<Integer>();
    }
    
    /**
     * push方法将元素压栈
     */
    private 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);
    }

    /**
     * stackData.pop()返回值赋值给value
     * value比对最小值,如果相等则将最小栈中的元素也弹出
     * @return 返回value
     */
    private int pop(){
    //因为最大值会先入stackData栈中所以只要判断此是否为空即可
        if (this.stackData.isEmpty()){
            throw  new RuntimeException("your stack is empty");
        }
        int value = this.stackData.pop();
        // 这样的判断也是为了避免NPE
        if (value==this.getmin()){
            this.stackMin.pop();
        }
        return value;
    }


    /**
     *getmin 获取最小元素
     * 利用peek方法,返回的元素并不会删除
     *
     * @return 最小栈元素
     */
    private int getmin(){
        if (this.stackMin.isEmpty()){
            throw new RuntimeException("your stack is emtry");
        }
        return this.stackMin.peek();
    }
}

方案二

在遇到较大的值时,压入上一次重复的较小值

这里写图片描述

具体实现

package com.demo.mystack;

import org.junit.Test;

import java.util.Stack;

/**
 * @author lorem
 * @date 2018/8/19
 */
public class MyStack1 {
    /**
     *  stackData 基本信息栈
     *  stackMin 最小栈
     */
    private Stack<Integer> stackData;
    private Stack<Integer> stackMin;

    /**
     * 通过初始化构造函数创建两个相关对象
     */
    public MyStack1(){
       this.stackData = new Stack<Integer>();
       this.stackMin = new Stack<Integer>();
    }

    @Test
    public void test(){
        MyStack1 myStack1 = new MyStack1();
        myStack1.push(2);
        myStack1.push(5);
        myStack1.push(4);
        myStack1.push(1);
        System.out.println(myStack1.pop());
        System.out.println(myStack1.getmin());
    }
    /**
     *
     * @param newNum
     *
     */
    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 emtry");
        }
        return this.stackMin.peek();
    }
}

总结

两种方案的最终目的都是让较大的数不压入最小值的栈中,方案一压栈时稍省空间,弹栈时稍费空间;方案二则压栈时费空间,弹栈时稍省空间。

因为方案一的实现在遇到较大值时,不会压入任何数据保持原样,所以在弹栈时需要做一个额外的判断,以免出现NPE。

int value = this.stackData.pop();
   // 这样的判断也是为了避免NPE
 if (value==this.getmin()){
	   this.stackMin.pop();
}

而方案二stackData与stackMin两个栈空间容量是等同的,所以,在弹栈时则比较顺便了

this.stackMin.pop();
this.stackData.pop();

也就是说,如果只是进行压栈操作而对弹栈操作较少的情况下用方案一;如果对弹栈操作较为频繁就用方案二。

- The End -

知识共享许可协议 本作品采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可。

原文地址:https://www.cnblogs.com/hoochanlon/p/9676623.html