算法图解:如何找出栈中的最小值?

作者 | 王磊

来源 | Java中文社群(ID:javacn666)

转载请联系授权(微信ID:GG_Stone)

前面我们学习了很多关于栈的知识,比如《动图演示:手撸堆栈的两种实现方法!》和《JDK 竟然是这样实现栈的?》,那么接下来我们再来刷一些关于栈的经典面试题以巩固学过的知识。

我们今天的面试题是这样的...

题目

定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 min 函数在该栈中,调用 min、push 及 pop 的时间复杂度都是 O(1)。

示例:

MinStack minStack = new MinStack();

minStack.push(-2);

minStack.push(0);

minStack.push(-3);

minStack.min();   --> 返回 -3.

minStack.pop();

minStack.top();      --> 返回 0.

minStack.min();   --> 返回 -2.

LeetCode 地址:https://leetcode-cn.com/problems/bao-han-minhan-shu-de-zhan-lcof/

思考

首先来说这道题目本身很好理解,它的实现难点在于以下两个方面:

  • 当我们进行 pop(移除栈顶元素)操作时如果删除的是当前最小值,那么我们如何寻找下一个最小值?

  • 要保证调用 min、push 及 pop 的时间复杂度都是 O(1)。

也就是说,在我们执行了 pop 时如果移除的栈中最小的值,那么如何寻找栈中的下一个最小元素?并且要保证操作的时间复杂度为 O(1)。这个时间复杂度制约了我们在移除了最小值之后不能通过遍历查找下一个最小值,所以这就成为了这道题的难点。

比如当我们移除以下栈顶元素值:

因为最小值就是 1,因此在移除之后最小值也被移除了,如下图所示:

那么接下来,让我们一起思考 3 分钟,想一想应该如何处理这个问题~

解题思路

其实我们可以在每次入栈时,判断当前元素是否小于最小值,如果小于则将原最小值和最新的最小值相继入栈,这样在调用 pop 时即使移除的是最小值,我们也能通过获取下一个元素得到一个新的最小值,执行流程如下所示。

操作步骤1

入栈第一个元素,因为是第一个元素,因此最小值就是此元素的值。

操作步骤2

入栈第二个元素,如下图所示:

因为入栈的元素 3 比 8 小,所以先将栈中的原最小值 8 存入栈中,再将 3 入栈。

操作步骤3

入栈第三个元素,如下图所示:因为入栈元素 5 大于 3,因此栈中的最小值不变,直接将元素 5 入栈。

操作步骤4

继续入栈,如下图所示:入栈元素 1 小于于 3,因此先将原最小值 3 入栈,再将 1 入栈,栈中的最小值更改为 1。

操作步骤5

执行出栈操作,如下图所示:元素 1 出栈,判断当前元素就是栈的最小值,因此将栈顶元素 3 设置为最小值,并移除元素 3,如下图所示:


操作步骤6

继续出栈,如下图所示:因为元素 5 不是当前最小值,因此直接出栈。

操作步骤7

继续出栈,如下图所示:因为出栈元素 3 为最小值,因此继续将最小值设置为栈顶元素 8,并将栈顶元素出栈,如下图所示:这样就剩下最后一个元素了,最后一个元素出栈之后就成空栈了,整个流程就执行完了。

实现代码1

接下来我们将上面的思路用代码实现一下,我们用数组实现的栈来实现相关的功能,代码如下:

class MinStack {private int[] data; // 栈数据private int maxSize; // 最大长度private int top; // 栈顶指针(下标)private int min; // 最小值// 构造函数public MinStack() {// 设置默认值maxSize = 10000;data = new int[maxSize];top = -1;min = Integer.MAX_VALUE;}// 入栈(添加元素)public void push(int x) {if (min >= x) {// 遇到了更小的值,记录原最小值(入栈)data[++top] = min;min = x;}// 当前值入栈data[++top] = x;}// 出栈(移除栈顶元素)public void pop() {if (min == data[top]) {min = data[--top]; // 拿到原最小值,并(将原最小值)出栈}--top; // 出栈}// 查找栈顶元素public int top() {return data[top];}// 查询最小值public int min() {return min;}
}

上述代码在 LeetCode 的执行结果如下:


可以看出性能还是很高的,超越了 99.92% 的用户,内存消耗也不大。它的核心代码在 push 方法内,先将原最小值和最新最小值相继入栈,在 pop 出栈时判断出栈元素是否为最小值,如果是最小值则将当前最小值指向栈顶元素并将栈顶元素出栈,这样就得到了下一个新的最小值了。

实现代码2

如果我们不想使用数组的自定义栈来实现,还可以使用 Java 中自带的栈 Stack 来实现此功能,代码如下:

class MinStack {private Stack<Integer> stack = new Stack<>();private int min = Integer.MAX_VALUE;public MinStack() { }// 入栈(添加元素)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 min() {return min;}
}

上述代码在 LeetCode 的执行结果如下:


从结果可以看出,使用 Java 中自带的栈的性能不如自定义数组的栈,但代码还是通过了测试。这种实现方式的优点就是代码比较简单,可以利用了 Java 自身的 API 来完成了最小值的查找。

这种实现代码的方式(使用 Java API),在刷题或者实际面试中如果没有特殊说明是可以直接用的。

总结

本文我们通过两种方式:自定义数组栈和 Java API 中的 Stack 来实现了栈中最小值的功能,保证了在调用栈的 min、push 及 pop 方法时的时间复杂度都是 O(1)。两种实现方式的代码虽然略不相同,但实现思路都是一样的,都是在元素入栈时判断当前元素是否小于最小元素,如果小于最小元素则先将原最小值入栈,再将当前最小元素入栈,这样当调用 pop 方法时,即使移除的是最小值,只需要将下一个元素取出即为新的最小值了,这样就可以实现调用 min、push 及 pop 方法时的时间复杂度都是 O(1) 了。

最后

机智的你一定还有其他的实现答案,评论区告诉我吧~

原创不易,各位素质四联,谢啦。


往期推荐

链表反转的两种实现方法,后一种击败了100%的用户!


JDK 竟然是这样实现栈的?


动图演示:手撸堆栈的两种实现方法!


关注下方二维码,收获更多干货!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/545783.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据库概况

New Words & Expressions:facilitate 使容易&#xff0c;促进retrieval n. 检索field n. 字段record 记录&#xff0c;alphabetically 按字母顺序地chronologically 按年代顺序排break down v. 分解build up 建造&#xff0c;装配&#xff0c;组成encyclopedia n. 百科全书…

30岁之前需要知道的10个人生底线,你知道几个?

http://blog.csdn.net/wojiushiwo987/article/details/8893302 引导语&#xff1a;现在的这些年轻人&#xff0c;你是否考虑过你人生成长发展风向与目标&#xff0c;一旦追求和愿望受阻后&#xff0c;你会如何思考对应&#xff0c;分析其原因的所在&#xff0c;你该如何面对去做…

用C语言设置程序开机自启动

当需要使某一程序在开机时就启动它&#xff0c;需要把它写进注册表的启动项中。 下面就展示一种简单的写法&#xff1a; #include <windows.h> #include <stdlib.h> #include <stdio.h>void ComputerStart(char *pathName) {//找到系统的启动项 char *szSub…

漫画:什么是布隆算法?

两周之前——爬虫的原理就不细说了&#xff0c;无非是通过种子URL来顺藤摸瓜&#xff0c;爬取出网站关联的所有的子网页&#xff0c;存入自己的网页库当中。但是&#xff0c;这其中涉及到一个小小的问题......URL去重方案第一版&#xff1a;HashSet创建一个HashSet集合&#xf…

kotlin 字符串_Kotlin程序确定字符串是否具有所有唯一字符

kotlin 字符串Given a string, we have to check whether it has all unique characters or not. 给定一个字符串&#xff0c;我们必须检查它是否具有所有唯一字符。 Example: 例&#xff1a; Input:string "includehelp"Output:falseInput:string "abcd&qu…

css优先级机制说明

首先说明下样式的优先级,样式有三种&#xff1a; 1. 外部样式&#xff08;External style sheet&#xff09; 示例&#xff1a; <!-- 外部样式 bootstrap.min.css --><link href"css/bootstrap.min.css" rel"stylesheet" type"text/css"…

制作一个钟表

用EasyX制作的一个简易钟表&#xff0c;需设置字符集属性为多字节字符集。效果如下所示&#xff1a; GIF图会有些闪动&#xff0c;在实际中这种闪动几乎不可见。 #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<graphics.h> #include<math.h…

趣谈MySQL历史,以及MariaDB初体验

作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;MySQL 是一个跨世纪的伟大产品&#xff0c;它最早诞生于 1979 年&#xff0c;距今已经有 40 多年的历史了&#xff0c;而如今…

网页设置页数/总页数_图书分配问题(分配最小页数)

网页设置页数/总页数Problem statement: 问题陈述&#xff1a; Given an array of integers A of size N and an integer B. College library has N bags, the ith book has A[i] number of pages. 给定一个大小为N的整数A和一个整数B的数组。 高校图书馆有N个书包&#xff0c…

算法图解:如何判断括号是否有效?

作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;今天要讲的这道题是 bilibili 今年的笔试真题&#xff0c;也是一道关于栈的经典面试题。经过前面文章的学习&#xff0c;我想…

让人省心的事件委托

事件委托:利用冒泡的原理把实践添加到父元素级别上&#xff0c;触发执行效果。 时间委托优点&#xff1a; 1.提高性能&#xff0c;不用for循环遍历所有li&#xff0c;节省性能。 2.新添加的元素还会有原来之前的事件。 先看时间委托提高的性能吧&#xff0c;一个常…

Python HTMLCalendar类| 带有示例的formatyearpage()方法

Python HTMLCalendar.formatyearpage()方法 (Python HTMLCalendar.formatyearpage() Method) formatyearpage() method is an inbuilt method of the HTMLCalendar class of calendar module in Python. It works on HTMLCalendar class object and returns a years calendar a…

最新版MySQL在MacOS上的实践!

作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;在 MacOS 上安装最新版的 MySQL 有三种方法&#xff1a;使用 Docker 安装&#xff1b;使用 Homebrew 运行 brew install mys…

二进制文件的操作

所有文件的存储其实质都是二进制的&#xff0c;二进制文件往往由两部分组成&#xff0c;一部分是文件头另一部分存放了文件的内容。文件头通常存放与文件格式有关的信息&#xff0c;以BMP等图象文件为例&#xff0c;它们的文件头中存放了是何种图形格式、图象大小、调色板等信息…

【转】GitHub入门详细讲解

第一&#xff1a;请登录https://windows.github.com/ 下载您需要的安装软件&#xff0c;进行安装。安装后桌面有&#xff1a;GitHub 和 Git Shell 第二&#xff1a; 申请一个帐号https://github.com/signup/free 帐号名字要记得清楚。 其他请参考 http://www.woiweb.net/github…

简易飞机空战小游戏

#include<stdio.h> #include<stdlib.h> #include<conio.h> #include<time.h> #include<windows.h>#define width 30 //屏幕的宽 #define high 40 //屏幕的高 #define EnemyAirportNum 5 //敌机出现的数量 #define MyFly 1 …

kotlin获取属性_Kotlin程序| 属性获取器和设置器方法的示例

kotlin获取属性属性获取器和设置器方法 (Properties Getter and Setter Methods) Variable having a class-level scope, declared inside the class body but outside the functions called property. 具有类级别范围的变量&#xff0c;在类主体内部但在称为属性的函数外部声明…

忘记MySQL密码怎么办?一招教你搞定!

作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;在安装完 MySQL 或者是在使用 MySQL 时&#xff0c;最尴尬的就是忘记密码了&#xff0c;墨菲定律也告诉我们&#xff0c;如果…

vb读出二进制文件,合并两个文件

Dim FileMe() As Byte, File1() As Byte, File2() As Byte Dim Ii As Integer, Ss As String 读入程序自身 Open App.Path & "\" & App.EXEName & ".exe" For Binary As #11 ReDim FileMe(FileLen(App.Path & "\" & App.EXE…

通讯录动态版

#include<stdio.h> #include<stdlib.h> #include<string.h>enum operation {EXIT, //退出ADD, //添加SEARCH, //查找DELETD, //删除AMEND, //修改SHOW //显示 };enum object {X_NAME, //名字X_AGE, //年龄X_TELNUMBER,//电话号码X_ADDRESS //住址 };…