Leetcode 栈 2
题号直达: 394 字符串解码 | 739 每日温度 | 84 最大矩形
LeetCode 394. 字符串解码 ↑
题目
给定一个经过编码的字符串 s,按规则 k[encoded_string] 解码并返回结果,其中 encoded_string 需要重复 k 次(k 为正整数)。可保证输入有效,且原始数据不含数字(数字只表示重复次数)。
测试样例
- 示例 1:
s = "3[a]2[bc]"
输出:"aaabcbc" - 示例 2:
s = "3[a2[c]]"
输出:"accaccacc" - 示例 3:
s = "2[abc]3[cd]ef"
输出:"abcabccdcdcdef" - 示例 4:
s = "abc3[cd]xyz"
输出:"abccdcdcdxyz"
提示
1 ≤ len(s) ≤ 30s由小写英文字母、数字和方括号'[]'组成s是有效输入,且整数范围在[1, 300]
代码(Python):栈展开(站主自主完成)
遇到普通字符(含数字、左括号)先入栈;遇到右括号 ] 时,弹栈得到本层 temp,再弹出前面的数字字符计算重复次数 repeat,把展开后的内容压回栈中。最终栈内容拼接即答案。
class Solution(object):
def decodeString(self, s):
"""
:type s: str
:rtype: str
"""
stack = []
for i in range(len(s)):
temp = []
num = []
repeat = 0
if s[i] != "]":
stack.append(s[i])
else:
while stack[-1] != "[":
temp.append(stack.pop()) # temp现在是要重复的内容
stack.pop() # 现在把 '[' pop掉
while stack[-1] in "0123456789":
num.append(stack.pop())
if not stack:
break
for j in range(len(num)):
repeat += 10 ** j * int(num[j])
for k in range(repeat):
temtemp = temp[:] # 这里必须要加[:],不然temtemp和temp指向的是同一个地址
while temtemp:
stack.append(temtemp.pop())
return "".join(stack)
思路解析
- 按层还原:每遇到
]就还原一层最近的[...],天然支持嵌套结构。 - 数字逆序处理:数字是从栈顶反向弹出的,
repeat += 10 ** j * int(num[j])可恢复正常十进制值。 - 复制列表:
temtemp = temp[:]保证每轮重复使用独立副本,避免就地pop破坏temp。 - 复杂度:与解码后字符串长度线性相关;在题目约束下可通过。
LeetCode 739. 每日温度 ↑
题目
给定整数数组 temperatures,answer[i] 表示第 i 天之后要等几天才会出现更高温度;若之后都不会升高,则 answer[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]
提示
1 ≤ len(temperatures) ≤ 10^530 ≤ temperatures[i] ≤ 100
代码(Python):单调栈(站主自主完成)
维护一个单调递减温度栈(配套下标栈 index)。扫描到新温度 temperatures[i] 时,若它高于栈顶温度,说明找到了栈顶那一天的“下一个更高温度”,即可计算天数差并弹栈;重复直到恢复单调,再把当前温度与下标入栈。
class Solution(object):
def dailyTemperatures(self, temperatures):
"""
:type temperatures: List[int]
:rtype: List[int]
"""
stack = []
index = []
ans = [0] * len(temperatures)
for i in range(len(temperatures)):
if not stack:
index.append(i)
stack.append(temperatures[i])
else:
if temperatures[i] <= stack[-1]:
stack.append(temperatures[i])
index.append(i)
else:
while temperatures[i] > stack[-1]:
ans[index[-1]] = i - index[-1]
index.pop()
stack.pop()
if not stack:
break
index.append(i)
stack.append(temperatures[i])
return ans
思路解析
- 单调性:
stack从栈底到栈顶递减,栈中每个位置都还没找到下一个更高温度。 - 弹栈更新:当当前温度更高时,持续弹出所有更低温度,并立刻计算答案
i - index[-1]。 - 下标作用:
index与stack同步,保证能 O(1) 取到“被更新那天”的位置。 - 复杂度:每个下标最多入栈、出栈一次,时间 O(n),空间 O(n)。
LeetCode 84. 柱状图中最大的矩形 ↑
题目
给定 n 个非负整数 heights 表示柱状图高度,每个柱子宽度为 1。求该柱状图中可勾勒出的最大矩形面积。
提示
1 ≤ len(heights) ≤ 10^50 ≤ heights[i] ≤ 10^4
示例图
题目示意图(0414.png`):
代码(Python)解法一:左右最近更小值 + 单调栈
先分别求每个柱子的左侧第一个更小元素下标 index_left[i] 与右侧第一个更小元素下标 index_right[i]。以 heights[i] 为高时,最大可扩展宽度为 index_right[i] - index_left[i] - 1,面积即 heights[i] * 宽度,遍历取最大。
class Solution(object):
def largestRectangleArea(self, heights):
"""
:type heights: List[int]
:rtype: int
"""
# 找左边界
index_left = []
index = []
stack = []
for i in range(len(heights)):
if not stack:
index_left.append(-1)
index.append(i)
stack.append(heights[i])
else:
while heights[i] <= stack[-1]:
stack.pop()
index.pop()
if not stack:
break
if not stack:
index_left.append(-1)
else:
index_left.append(index[-1])
stack.append(heights[i])
index.append(i)
# 找右边界
index_right = [0] * len(heights)
index = []
stack = []
for i in range(len(heights) - 1, -1, -1):
if not stack:
index_right[i] = len(heights)
index.append(i)
stack.append(heights[i])
else:
while heights[i] <= stack[-1]:
stack.pop()
index.pop()
if not stack:
break
if not stack:
index_right[i] = len(heights)
else:
index_right[i] = index[-1]
stack.append(heights[i])
index.append(i)
# 根据左右边界找最大值
max_area = 0
for i in range(len(heights)):
area = heights[i] * (index_right[i] - index_left[i] - 1)
if area > max_area:
max_area = area
return max_area
思路解析(解法一)
- 单调栈边界:左扫维护递增栈可得到左侧最近更小元素;右扫同理得到右侧最近更小元素。
- 面积公式:固定高度
heights[i]时,左右边界之外都是更小值,故可覆盖的连续宽度是right - left - 1。 - 重复高度处理:用
<=弹栈可保证边界严格指向“更小值”而非“相等值”,避免宽度重复计算混乱。 - 复杂度:每个下标在左右两次扫描中最多进栈出栈各一次,总时间 O(n),空间 O(n)。
代码(Python)解法二:单调栈 + 尾部哨兵 0(一次扫描)
在末尾追加一个高度 0 作为哨兵,强制把栈中剩余柱子一次性结算。栈内存放下标,且对应高度单调不降;当遇到更矮柱子时,持续弹出并把被弹出的柱高作为当前矩形高度,宽度由“当前下标 i 与弹出后新的栈顶”确定。
class Solution(object):
def largestRectangleArea(self, heights):
heights.append(0)
n = len(heights)
stack = []
ans = 0
for i in range(n):
while stack and heights[i] < heights[stack[-1]]:
x = stack.pop()
area = (i - stack[-1] - 1 if stack else i) * heights[x]
ans = max(ans, area)
stack.append(i)
return ans
思路解析(解法二)
- 哨兵作用:
heights.append(0)保证最后会触发弹栈,避免循环后再单独清栈。 - 宽度计算:弹出
x后,若栈非空,左边界是stack[-1],宽度是i - stack[-1] - 1;若栈空,说明左侧都不小于heights[x],宽度是i。 - 与解法一关系:本质仍是找“左右最近更小值”,只是把边界计算隐含在弹栈时完成。
- 复杂度:每个下标最多入栈、出栈一次,时间 O(n),空间 O(n)。

浙公网安备 33010602011771号