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

题记:

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

实现 MinStack 类:

  • MinStack() 初始化堆栈对象。
  • void push(int val) 将元素val推入堆栈。
  • void pop() 删除堆栈顶部的元素。
  • int top() 获取堆栈顶部的元素。
  • int getMin() 获取堆栈中的最小元素。

示例 1:

输入
[“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.

提示:

  • -2 ^ 31 <= val <= 2 ^ 31 - 1
  • pop、top 和 getMin 操作总是在 非空栈 上调用
  • push, pop, top, and getMin最多被调用 3 * 104 次

题目来源:
作者:LeetCode
链接:https://leetcode.cn/leetbook/read/top-interview-questions-easy/xnkq37/
来源:力扣(LeetCode)

解题方法:

一:不进行最小值更新

class MinStack {
    /**
     * 数组结构实现
     */
    private $arr = null;
    private $count = 0;
    function __construct() {
        $this->arr = [];
    }

    /**
     * @param Integer $val
     * @return NULL
     * 将元素 val 推入栈中。
     */
    function push($val) {
       $this->arr[]=$val;
        $this->count++;
    }

    /**
     * @return NULL
     * 删除栈顶的元素。
     */
    function pop() {
        array_pop($this->arr);
        $this->count--;
    }

    /**
     * @return Integer
     * 获取栈顶元素
     */
    function top() {
        return $this->arr[$this->count-1];
    }

    /**
     * @return Integer
     * 检索栈中的最小元素
     */
    function getMin() {
        return min($this->arr);
    }
}

/**
 * Your MinStack object will be instantiated and called as such:
 * $obj = MinStack();
 * $obj->push($val);
 * $obj->pop();
 * $ret_3 = $obj->top();
 * $ret_4 = $obj->getMin();
 */

二:最小值进行更新(官方方法)

<?php 
class MinStack {
    /**
     * 数组结构实现
     */
    function __construct() {
        $this->list = [];
        $this->minList = [];
    }

    /**
     * @param Integer $val
     * @return NULL
     * 将元素 val 推入栈中
     */
    function push($val) {
        array_push($this->list, $val);
        //对最小值数组进行判断,为空直接插入,不为空则判断大小决定是否插入
        if(count($this->minList) == 0)  
        {
            array_push($this->minList, $val);
        }else{
            if(end($this->minList) >= $val)
            {
                array_push($this->minList, $val);
            }
        }
    }

    /**
     * @return NULL
     * 删除栈顶的元素
     */
    function pop() {
        $result = array_pop($this->list);
        if($result == end($this->minList))
        {   
            //删除的栈顶元素刚好等于最小元素时,最小元素数组也要进行删除
            array_pop($this->minList);
        }
    }

    /**
     * @return Integer
     * 获取栈顶元素 
     */
    function top() {
        return end($this->list);
    }

    /**
     * @return Integer
     * 检索栈中的最小元素
     */
    function getMin() {
        return end($this->minList);
    }
}

/**
 * Your MinStack object will be instantiated and called as such:
 * $obj = MinStack();
 * $obj->push($val);
 * $obj->pop();
 * $ret_3 = $obj->top();
 * $ret_4 = $obj->getMin();
 */

其他方法:主要讲思路(java)

三:使用辅助类解决

这道题让我们自定义一个栈,有push,pop,top,min四个函数。这题和官方的Stack相比就多了一个min函数。栈的实现我们可以使用链表,先来定义一个链表类

class ListNode {
    public int val;
    public int min;//最小值
    public ListNode next;

    public ListNode(int val, int min, ListNode next) {
        this.val = val;
        this.min = min;
        this.next = next;
    }
}

这里对链表的操作永远都是链表的头,假如往栈中加入3→2→5→4,画个图来看一下使用链表怎么操作的
在这里插入图片描述
代码比较简单,来看下

class MinStack {
    //链表头,相当于栈顶
    private ListNode head;

    //压栈,需要判断栈是否为空
    public void push(int x) {
        if (empty())
            head = new ListNode(x, x, null);
        else
            head = new ListNode(x, Math.min(x, head.min), head);
    }

    //出栈,相当于把链表头删除
    public void pop() {
        if (empty())
            throw new IllegalStateException("栈为空……");
        head = head.next;
    }

    //栈顶的值也就是链表头的值
    public int top() {
        if (empty())
            throw new IllegalStateException("栈为空……");
        return head.val;
    }

    //链表中头结点保存的是整个链表最小的值,所以返回head.min也就是
    //相当于返回栈中最小的值
    public int getMin() {
        if (empty())
            throw new IllegalStateException("栈为空……");
        return head.min;
    }

    //判断栈是否为空
    private boolean empty() {
        return head == null;
    }
}

class ListNode {
    public int val;
    public int min;//最小值
    public ListNode next;

    public ListNode(int val, int min, ListNode next) {
        this.val = val;
        this.min = min;
        this.next = next;
    }
}

上面解决方式是使用一个辅助的类,实际上如果使用辅助类,我们也可以使用官方提供的栈,像下面这样。

class MinStack {
    private Stack<StackNode> stack = new Stack<>();

    //压栈
    public void push(int x) {
        if (empty()) {
            stack.push(new StackNode(x, x));
        } else {
            stack.push(new StackNode(x, Math.min(x, getMin())));
        }
    }

    //出栈
    public void pop() {
        if (empty())
            throw new IllegalStateException("栈为空……");
        stack.pop();
    }

    public int top() {
        if (empty())
            throw new IllegalStateException("栈为空……");
        return stack.peek().val;
    }

    public int getMin() {
        if (empty())
            throw new IllegalStateException("栈为空……");
        return stack.peek().min;
    }

    //判断栈是否为空
    private boolean empty() {
        return stack.isEmpty();
    }
}

class StackNode {
    public int val;
    public int min;

    public StackNode(int val, int min) {
        this.val = val;
        this.min = min;
    }
}

四:使用单个栈解决

也可以使用官方提供的栈,当压栈的值小于栈中最小值时,先把最小值入栈,然后再把需要压栈的值入栈,最后再更新栈中最小值。如果压栈的值大于栈中最小值的时候,直接压栈,这里就以[6,2,1,4]分别入栈来看一下
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这是压栈的过程,出栈的时候如果出栈的值等于最小值,说明最小值已经出栈了,要更新最小值,估计直接看代码会更明白一些

class MinStack {//push方法可能会加入很多min
    int min = Integer.MAX_VALUE;
    Stack<Integer> stack = new Stack<>();

    public void push(int x) {
        //如果加入的值小于最小值,要更新最小值
        if (x <= min) {
            stack.push(min);
            min = x;
        }
        stack.push(x);
    }

    public void pop() {
        //如果把最小值出栈了,就更新最小值
        if (stack.pop() == min)
            min = stack.pop();
    }

    public int top() {
        return stack.peek();
    }

    public int getMin() {
        return min;
    }
}

这种方式虽然也能解决,但如果压栈的值一直递减的话,栈中会压入很多的min,实际上我们还可以在改一下,栈中压入的是需要压栈的值和最小值的差值,这样就不会压入min了,看下代码

public class MinStack {
    long min;
    Stack<Long> stack = new Stack<>();

    public void push(int x) {
        if (stack.isEmpty()) {
            stack.push(0L);
            min = x;
        } else {
            //这里入栈的是入栈的值和最小值的差值,有可能为负,也有可能为正。
            stack.push(x - min);
            if (x < min)
                min = x;
        }
    }

    public void pop() {
        if (stack.isEmpty())
            return;
        long pop = stack.pop();
        //因为入栈的是差值,当出栈的为负数的时候,说明栈中最小值已经出栈了,
        //这里要重新更新最小值
        if (pop < 0)
            min -= pop;
    }

    public int top() {
        long top = stack.peek();
        if (top > 0) {
            //栈顶元素如果是正的,说明栈顶元素压栈的时候是比栈中最小值大的,根据
            //top=x - min,可以计算x=top+min
            return (int) (top + min);
        } else {
            //当栈顶元素是负数的时候,说明栈顶元素压栈的时候是比栈中最小值小的,
            //而压栈完之后他会更新最小值min,所以如果在使用上面公式肯定是不行
            //的。如果栈顶元素压栈的时候比最小值小,他会更新最小值,这个最小值
            //就是我们要压栈的值,所以这里直接返回min就行了。
            return (int) (min);
        }
    }

    public int getMin() {
        return (int) min;
    }
}

五:使用双栈解决(同方法二)

这个代码比较简洁,就不在说了,直接看下代码

class MinStack {
    //栈1存放的是需要压栈的值
    Stack<Integer> stack1 = new Stack<>();
    //栈2存放的是最小值
    Stack<Integer> stack2 = new Stack<>();

    public void push(int x) {
        stack1.push(x);
        if (stack2.empty() || x <= getMin())
            stack2.push(x);
    }

    public void pop() {
        //如果出栈的值等于最小值,说明栈中的最小值
        //已经出栈了,因为stack2中的栈顶元素存放的
        //就是最小值,所以stack2栈顶元素也要出栈
        if (stack1.pop() == getMin())
            stack2.pop();
    }

    public int top() {
        return stack1.peek();
    }

    public int getMin() {
        return stack2.peek();
    }
}

方法来源:
作者:数据结构和算法
链接:https://leetcode.cn/leetbook/read/top-interview-questions-easy/xnkq37/?discussion=Qgb0Ea
来源:力扣(LeetCode)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值