小团队兼职做网站商务网站规划与建设的目的
2026/4/6 5:43:19 网站建设 项目流程
小团队兼职做网站,商务网站规划与建设的目的,值得买 wordpress,可以做产品宣传的网站“最小栈”#xff08;LeetCode 155题#xff09;作为一道非常经典的试金石。它不涉及复杂的动态规划或图论#xff0c;却能精准地考察候选人对数据结构的理解深度以及空间换时间这一核心算法思想的掌握程度。 题目要求看似简单#xff1a;设计一个栈#xff0c;支持 push…“最小栈”LeetCode 155题作为一道非常经典的试金石。它不涉及复杂的动态规划或图论却能精准地考察候选人对数据结构的理解深度以及空间换时间这一核心算法思想的掌握程度。题目要求看似简单设计一个栈支持 push、pop、top 操作并能在常数时间O(1)内检索到最小元素 getMin。很多候选人在看到题目时往往忽略了O(1)这个关键约束直接给出了一个功能正确但性能不达标的方案。今天我们就从面试官的视角来剖析这道题从O(N)到O(1)的进化过程。第一阶段直觉与妥协 (暴力解法)当被问到“如何实现一个支持获取最小值的栈”时大多数人的第一直觉是利用 JavaScript 数组的原生方法。既然栈的本质是先进后出LIFO那么 push、pop 和 top 都很容易实现。对于 getMin最朴素的想法是既然我要找最小值那就遍历整个数组好了。以下是这类回答的典型代码实现JavaScript// ES5 构造函数 const MiniStack function() { this.stack []; // 存储数据的数组 } MiniStack.prototype.push function(x) { this.stack.push(x); } MiniStack.prototype.pop function() { return this.stack.pop(); } MiniStack.prototype.top function() { if(!this.stack || !this.stack.length){ return; } return this.stack[this.stack.length-1]; } // 暴力解法核心遍历查找 O(N) MiniStack.prototype.getMin function() { let minValue Infinity; // 初始化为无穷大 const { stack } this; // 遍历整个栈寻找最小值 for(let i 0; i stack.length; i){ if(stack[i] minValue){ minValue stack[i]; } } return minValue; }面试官点评这份代码从功能上来说是正确的。它利用了 JS 数组模拟栈push、pop、top 的时间复杂度确实是O(1)。但是getMin 方法的实现存在致命弱点它的时间复杂度是O(N)。随着栈内元素数量N的增加获取最小值的耗时将线性增长。如果在高频调用的场景下这种遍历操作是不可接受的性能瓶颈。在面试中这只能算是一个“勉强及格”的答案因为它没有体现出任何算法优化的思维。第二阶段思维跃迁 (空间换时间)如何将O(N)优化为O(1)这需要我们转换思维。在暴力解法中我们每次调用 getMin 都要重新计算。也就是我们“忘记”了之前的比较结果。如果我们能有一种机制能够“记住”随着数据入栈过程中每一个状态下的最小值那就不需要回头遍历了。这里引入算法设计中极重要的思想空间换时间。我们需要引入一个辅助栈Auxiliary Stack。数据栈 (stack)负责常规的数据存储维持栈的正常逻辑。辅助栈 (stack2)负责同步存储当前数据栈对应的最小值。核心策略辅助栈的栈顶永远存储着当前数据栈中所有元素的最小值。这实际上维护了一个非严格单调递减的序列。第三阶段完美实现 (双栈协同)有了辅助栈的思路接下来的难点在于如何保持两个栈的状态同步我们需要处理好两个关键逻辑入栈 (Push)新元素进来了辅助栈存不存出栈 (Pop)数据栈弹出了辅助栈要不要跟着弹以下是经过逻辑修正后的O(1)完美实现JavaScriptconst MiniStack function() { this.stack []; // 数据栈 this.stack2 []; // 辅助栈单调栈栈顶即为最小值 } // O(1) MiniStack.prototype.push function(x) { // 1. 数据栈必须入栈 this.stack.push(x); // 2. 辅助栈入栈逻辑 // 如果辅助栈为空或者新元素 x 小于等于 辅助栈栈顶则入辅助栈 // 注意这里必须是 不能只是 否则会有重复最小值丢失的问题 if(this.stack2.length 0 || x this.stack2[this.stack2.length-1]) { this.stack2.push(x); } } // O(1) MiniStack.prototype.pop function() { // 1. 数据栈弹出 const val this.stack.pop(); // 2. 辅助栈同步逻辑 // 如果弹出的元素等于辅助栈栈顶元素说明最小值被移除辅助栈也要弹出 if(val this.stack2[this.stack2.length-1]) { this.stack2.pop(); } return val; } // O(1) MiniStack.prototype.top function() { return this.stack[this.stack.length-1]; } // O(1) MiniStack.prototype.getMin function() { // 直接返回辅助栈栈顶无需遍历 return this.stack2[this.stack2.length-1]; }深度解析1. Push 操作的去重与同步在 push 方法中判断条件 x this.stack2[top] 至关重要。为什么要判断大小我们只关心比当前最小值更小或相等的数。如果新来的数比当前最小值还大它绝不可能是当前的最小值因此不需要压入辅助栈。这保证了辅助栈的单调递减特性。为什么要包含等于这是一个常见的坑。假设入栈序列为 [5, 2, 2]。如果不包含等于辅助栈通过判断只存入第一个 2。当数据栈弹出最上面的 2 时代码会误以为最小值被移除了导致辅助栈唯一的 2 也被弹出。此时数据栈里还剩一个 2但辅助栈里的最小值变成了 5。这就产生了 Bug。因此重复的最小值必须同时压入辅助栈。2. Pop 操作的同步在 pop 方法中我们对比数据栈弹出的值与辅助栈栈顶的值。只有当两者相等时才弹出辅助栈。这意味着我们移除的正是当前的最小值。辅助栈弹出后新的栈顶自然就变成了“次小值”即之前的最小值完美还原了历史状态。3. GetMin 的极致性能由于辅助栈的精心维护其栈顶永远是全局最小值。getMin 变成了简单的数组索引访问没有任何循环时间复杂度稳稳落在 O(1)总结辅助栈就像是数据栈的“历史快照”索引。无论数据栈怎么进出辅助栈的栈顶始终指向“当前存活数据中的最小者”。这种设计将 getMin 的复杂度从线性阶降维到了常数阶。在实际面试中写出代码往往只占 60% 的分数。剩下的 40% 取决于你能否清晰地解释“为什么引入辅助栈”、“如何处理重复最小值”以及“空间与时间的权衡”。算法不仅仅是背诵代码更是对数据流动和资源消耗的精准控制。如果这篇文章对你有帮助的话就请你点个赞吧原文 https://juejin.cn/post/75995852

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询