一、栈:数据结构界的俄罗斯套娃
想象你有一摞盘子,每次只能拿最上面的那个,这就是栈的精髓——后进先出(LIFO) !这个简单的规则让栈成为算法界的变形金刚:
- 叠盘子神器:新盘子压在顶部,取盘子只能从顶部开始
二、括号匹配:
给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 每个右括号都有一个对应的相同类型的左括号。
示例 1:
输入: s = "()"
输出: true
示例 2:
输入: s = "()[]{}"
输出: true
示例 3:
输入: s = "(]"
输出: false
示例 4:
输入: s = "([])"
输出: true
问题场景:验证"({[]})"这样的括号串是否合法。 [20. 有效的括号 - 力扣(LeetCode)](url)
var isValid = function(s) {
const map = { "(": ")", "[": "]", "{": "}" };
const stack = []; // 仓库
for (const char of s) {
if (char in map) { // 入库!
stack.push(char);
} else {
if (!stack.length) return false;
const top = stack.pop();
if (map[top] !== char) return false; // 配对错误!
}
}
return !stack.length;
};
执行示例:
以 s = "()[]{}" 为例:
(→ 入栈:stack = ['('])→ 弹出(,obj['('] === ')'→ 匹配[→ 入栈:stack = ['[']]→ 弹出[,obj['['] === ']'→ 匹配{→ 入栈:stack = ['{']}→ 弹出{,obj['{'] === '}'→ 匹配- 栈空 → 返回
true精彩看点:
- 用对象建立"左括号→右括号"的红线本
- 左括号自觉叠高高,右括号来时检查栈顶是不是自己的真命天子
- 最后要是还有备胎滞销,整个字符串就崩盘啦!
三、每日温度:预言家的魔法书
给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0 来代替。
示例 1:
输入: temperatures = [73,74,75,71,69,72,76,73]
输出: [1,1,4,2,1,1,0,0]
示例 2:
输入: temperatures = [30,40,50,60]
输出: [1,1,1,0]
示例 3:
输入: temperatures = [30,60,90]
输出: [1,1,0]
问题场景:给定一串温度,要算出每个日子还要等多久才能遇到更暖和的天。这时候单调栈闪亮登场,它就像个严格的天气预言家:
// 温度监察大队出动!
var dailyTemperatures = function(temps) {
const stack = []; // 存储待解决日期的索引
const result = new Array(temps.length).fill(0);
for (let i = 0; i < temps.length; i++) {
// 遇到暖流,开始清算!
while (stack.length && temps[i] > temps[stack[stack.length-1]]) {
const idx = stack.pop();
result[idx] = i - idx; // 计算蹲守天数
}
stack.push(i); // 新温度加入监察队列
}
return result;
};
以 temps = [73, 74, 75, 71, 69, 72, 76, 73] 为例:
当前索引 i | 当前温度 | 栈 stack (索引) | 清算操作 | 结果数组 result |
|---|---|---|---|---|
| 0 | 73 | [0] | 无 | [0,0,0,0,0,0,0,0] |
| 1 | 74 | [] | 74>73 → 弹出0, result[0]=1-0=1 | [1,0,0,0,0,0,0,0] |
| 2 | 75 | [] | 75>74 → 弹出1, result[1]=2-1=1 | [1,1,0,0,0,0,0,0] |
| 3 | 71 | [2] | 71<75 → 无弹出 | [1,1,0,0,0,0,0,0] |
| 4 | 69 | [2,3] | 69<71 → 无弹出 | [1,1,0,0,0,0,0,0] |
| 5 | 72 | [2] | 72>69 → 弹出4, result[4]=5-4=1 72>71 → 弹出3, result[3]=5-3=2 | [1,1,0,2,1,0,0,0] |
| 6 | 76 | [] | 76>72 → 弹出5, result[5]=6-5=1 76>75 → 弹出2, result[2]=6-2=4 | [1,1,4,2,1,1,0,0] |
| 7 | 73 | [6] | 73<76 → 无弹出 | [1,1,4,2,1,1,0,0] |
最终结果:[1, 1, 4, 2, 1, 1, 0, 0]
算法特性
- 时间复杂度:O(n)
每个索引最多入栈和出栈一次,遍历一次数组。 - 空间复杂度:O(n)
栈的空间(最坏情况全部入栈) [739. 每日温度 - 力扣(LeetCode)](url)
魔法揭秘:
1. 递减特工队:栈里藏着越来越冷的温度下标
2.触发清算:当暖流来袭时,前面的冷日子终于等到春天
3. 记忆大师:result数组记录每个寒冬的等待时长
极端天气应对:
- 连续升温:像[73,74,75,76],每个元素入栈后立即被弹出
- 持续降温:像[76,75,74,73],所有元素保留在栈中,结果数组全为0
五、栈的核心生存哲学
这位叠罗汉大师的终极奥义,就是"后进先出俱乐部"的铁律——最后进来的家伙总是第一个被弹出去。这种特性让它在处理嵌套结构时如鱼得水:
匹配型问题:像玩多米诺骨牌,推倒一个检查一个
单调优化:用空间换时间,把O(n²)暴力算法优化成O(n)优雅舞步