网站首页 > 技术文章 正文
在栈的各种操作中,我们通常需要push(入栈)、pop(出栈)、top(获取栈顶元素)这些基本功能。但如果要快速获取栈中的最小元素呢?如果每次都遍历整个栈来找最小值,效率就太低了。今天就来教你一个妙招——用双栈法实现一个支持O(1)时间复杂度查询最小值的栈,轻松应对面试中的这类问题!
问题:普通栈的痛点
普通的栈结构虽然能高效完成入栈、出栈操作,但要获取栈中的最小元素却很麻烦。比如有一个栈,元素依次为[3, 2, 4, 1, 5],要找到最小值1,只能从头到尾遍历一遍,时间复杂度是O(n)。如果需要频繁查询最小值,这种方法显然不够高效。
题目要求我们设计一个栈,除了支持push、pop、top操作外,还能通过min函数在O(1)时间内返回当前栈中的最小元素。
核心思路:双栈协作,各司其职
解决这个问题的关键是引入辅助栈。我们需要两个栈:
- 数据栈(Data):负责存储所有元素,实现基本的push、pop、top操作。
- 辅助栈(Min):专门记录对应数据栈状态下的最小值,让min函数可以直接取栈顶元素。
具体操作规则如下:
- 入栈(push)时:
- 直接将元素压入数据栈。
- 对于辅助栈:
- 如果辅助栈为空,直接将当前元素压入。
- 如果辅助栈不为空,比较当前元素与辅助栈顶元素:
- 若当前元素更小,就将其压入辅助栈。
- 若当前元素更大或相等,就将辅助栈顶元素再次压入(保证辅助栈与数据栈长度一致,方便后续操作)。
- 出栈(pop)时:
- 数据栈和辅助栈同时弹出栈顶元素(因为两者长度始终一致)。
- 获取栈顶元素(top)时:
- 直接返回数据栈的栈顶元素。
- 获取最小值(min)时:
- 直接返回辅助栈的栈顶元素(因为辅助栈顶始终记录当前最小值)。
举个例子,一目了然
我们用一个具体的入栈过程来演示: 假设依次入栈的元素为:3 → 2 → 4 → 1 → 5
- 入栈3:
- 数据栈:[3]
- 辅助栈为空,压入3 → [3]
- 当前min为3
- 入栈2:
- 数据栈:[3, 2]
- 2 < 辅助栈顶3,压入2 → [3, 2]
- 当前min为2
- 入栈4:
- 数据栈:[3, 2, 4]
- 4 > 辅助栈顶2,压入2 → [3, 2, 2]
- 当前min仍为2
- 入栈1:
- 数据栈:[3, 2, 4, 1]
- 1 < 辅助栈顶2,压入1 → [3, 2, 2, 1]
- 当前min为1
- 入栈5:
- 数据栈:[3, 2, 4, 1, 5]
- 5 > 辅助栈顶1,压入1 → [3, 2, 2, 1, 1]
- 当前min仍为1
此时如果执行pop操作(弹出5):
- 数据栈变为:[3, 2, 4, 1]
- 辅助栈变为:[3, 2, 2, 1]
- min仍为1(辅助栈顶)
再执行pop操作(弹出1):
- 数据栈变为:[3, 2, 4]
- 辅助栈变为:[3, 2, 2]
- min变为2(辅助栈顶)
通过这个例子可以清晰地看到,辅助栈始终同步记录着数据栈对应的最小值,实现了O(1)时间查询。
代码实现:C++和Python版本
C++版本
#include <stack>
using namespace std;
class Solution {
public:
// 入栈操作
void push(int value) {
// 数据栈直接压入元素
Data.push(value);
// 辅助栈处理:空栈直接压入,否则压入较小值
if (Min.empty()) {
Min.push(value);
} else {
// 若当前值更小则压入当前值,否则压入原最小值(保持栈顶为当前最小值)
int min_val = Min.top() < value ? Min.top() : value;
Min.push(min_val);
}
}
// 出栈操作
void pop() {
// 数据栈和辅助栈同时弹出
if (!Data.empty()) {
Data.pop();
Min.pop();
}
}
// 获取栈顶元素
int top() {
return Data.top();
}
// 获取最小值
int min() {
return Min.top();
}
private:
stack<int> Data; // 数据栈:存储所有元素
stack<int> Min; // 辅助栈:存储对应状态的最小值
};
Python版本
class Solution:
def __init__(self):
# 初始化数据栈和辅助栈
self.Data = [] # 存储所有元素
self.Min = [] # 存储对应状态的最小值
def push(self, node):
"""入栈操作"""
self.Data.append(node)
if self.Min:
# 辅助栈非空时,压入当前最小值(原最小值与新元素的较小者)
if self.Min[-1] > node:
self.Min.append(node)
else:
self.Min.append(self.Min[-1])
else:
# 辅助栈为空时,直接压入当前元素
self.Min.append(node)
def pop(self):
"""出栈操作"""
if not self.Data:
return None
# 数据栈和辅助栈同时弹出栈顶元素
self.Min.pop()
return self.Data.pop()
def top(self):
"""获取栈顶元素"""
if not self.Data:
return None
return self.Data[-1]
def min(self):
"""获取当前最小值"""
if not self.Min:
return None
return self.Min[-1]
为什么这种方法高效?
- 时间复杂度:push、pop、top、min四个操作的时间复杂度都是O(1),因为每个操作都只涉及栈的顶部元素,没有遍历或其他耗时操作。
- 空间复杂度:O(n),需要额外的辅助栈存储最小值,最坏情况下(元素单调递增),辅助栈与数据栈存储相同元素,空间翻倍。但相比时间效率的提升,这种空间开销通常是可接受的。
总结
带min函数的栈是面试中的经典问题,核心思路是用双栈协作:数据栈负责存储元素,辅助栈同步记录对应状态的最小值。这种方法既保证了所有操作的高效性,又易于理解和实现。
掌握了这个技巧,不仅能轻松解决这类问题,还能加深对栈结构和空间换时间思想的理解。下次面试遇到类似问题,就再也不怕啦!
猜你喜欢
- 2025-09-14 在Excel用max()/min()函数代替IF()函数的多分支判断的一个小案例
- 2025-09-14 【C语法硬核20讲】07 宏与预处理:写出安全宏
- 2025-09-14 用数学的方法理解Photoshop混合模式——变暗模式
- 2025-09-14 这4个变态的Excel函数公式,却好用的很
- 2025-09-14 常用函数示例_常用函数示例大全
- 2025-09-14 Excel教程:常用的Excel统计函数汇总
- 2025-09-14 办公小技巧:用好Excel 2019新函数为办公提速
- 2025-09-14 python常用得内置函数解析——min()函数
- 2025-07-10 Python 元组(Tuple)详解(python元组用来做什么)
- 2025-07-10 Excel如何去除前导0,中间和末尾的0不去除?送大家一条通用公式
- 最近发表
- 标签列表
-
- cmd/c (90)
- c++中::是什么意思 (84)
- 标签用于 (71)
- 主键只能有一个吗 (77)
- c#console.writeline不显示 (95)
- pythoncase语句 (88)
- es6includes (74)
- sqlset (76)
- apt-getinstall-y (100)
- node_modules怎么生成 (87)
- chromepost (71)
- flexdirection (73)
- c++int转char (80)
- mysqlany_value (79)
- static函数和普通函数 (84)
- el-date-picker开始日期早于结束日期 (76)
- js判断是否是json字符串 (75)
- c语言min函数头文件 (77)
- asynccallback (71)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- java (73)
- js数组插入 (83)
- mac安装java (72)
- 无效的列索引 (74)