Day10,Hot100(栈,堆)
栈
(1)20. 有效的括号
d = {')':'(', ']':'[', '}':'{'}
stack = []
for i in s:
# 遇到左括号,入栈
if i in '([{':
stack.append(i)
# 遇到右括号,出栈左括号,看是否有匹配的左括号
else:
if not stack: # 栈空的时候遇到右括号,则一定不能匹配
return False
if d[i] == stack[-1]: # 右括号与前面的左括号匹配
stack.pop()
else:
return False
if not stack: # 栈为空, 说明全部都匹配成功
return True
return False
(2)155. 最小栈
维护一个最小栈,存储当前栈的最小值
栈尾总是当前栈的最小值
- push时需要判断新加入的值是否小于
min_st[-1]
- pop的时候需要判断pop的元素是否是
min_st[-1]
class MinStack:
def __init__(self):
self.stack = []
self.min_st = [] # 保存stack中的最小值
def push(self, val: int) -> None:
self.stack.append(val)
if not self.min_st or self.min_st[-1] >= val:
self.min_st.append(val)
def pop(self) -> None:
if self.stack.pop() == self.min_st[-1]:
self.min_st.pop()
def top(self) -> int:
return self.stack[-1]
def getMin(self) -> int:
return self.min_st[-1]
(3)394. 字符串解码
示例:3[a2[c]]
递
3[ a2[c] ]
a2[c]
2[c]
归
2[c]
-->cc
a2[c]
-->acc
3[ a2[c] ]
-->acc acc acc
class Solution:
def decodeString(self, s: str) -> str:
stack = []
res = '' # 当前解码的字符串
multi = 0 # 当前的倍数
for c in s:
if c == '[':
stack.append((res, multi)) # 数字之前的结果,以及数字
multi = 0
res = ''
elif '0' <= c <= '9':
multi = multi * 10 + int(c)
elif c == ']':
last_res, cur_multi = stack.pop()
res = last_res + cur_multi * res
else:
res += c
return res
(4)739. 每日温度
单调栈
- 及时去掉无用数据
- 保证栈中元素有序
方法一:从左往右遍历:
- 栈内保留还没有找到答案的元素
- 当元素大于栈顶时出栈栈顶, 每次在出栈的时候更新答案,
class Solution:
def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
n = len(temperatures)
ans = [0] * n
st = [] # 存储数组下标
for i in range(n):
t = temperatures[i]
while st and t > temperatures[st[-1]]:
j = st.pop()
ans[j] = i - j
st.append(i)
return ans
方法二:从右往左遍历:
- 逐步出栈,直到遇到符合条件的温度,更新答案
class Solution:
def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
n = len(temperatures)
ans = [0] * n
st = [] # 存储数组下标
for i in range(n-1, -1, -1):
t = temperatures[i]
# 如果栈顶元素小于当天温度,则持续出栈,直到找到
while st and t >= temperatures[st[-1]]:
st.pop()
if st:
ans[i] = st[-1] - i
st.append(i)
return ans
堆
堆是一颗完全二叉树,即每一层都被完全填满,最后一层从左到右填充
最大堆
- 父节点 >= 子结点,堆顶为最大值
10
/ \
8 9
/ \ /
3 7 4
最小堆
- 父节点 <= 子结点,堆顶为最小值
3
/ \
7 4
/ \
8 9
堆的插入和删除操作的时间复杂度为 O(log n)
,n为堆的元素个数
heapq 模块默认是 最小堆,要实现最大堆,可以将所有元素取反
(1)215. 数组中的第K个最大元素
最小堆
- 维护一个大小为
k
的最小堆 - 最终堆顶就是第
k
大的元素
import heapq
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
min_heap = nums[:k]
heapq.heapify(min_heap) # 使用前k个元素构建最小堆
# 从第 k+1 个元素开始遍历
for num in nums[k:]:
if num > min_heap[0]:
heapq.heappop(min_heap) # 弹出堆顶元素
heapq.heappush(min_heap, num) # 插入新的元素
return min_heap[0] # 堆顶元素就是第 K 个最大的元素
最大堆
- 将整个数组堆化,逐个弹出堆顶
- 第 k 个堆顶就是第 k 大的元素
import heapq
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
nums = [-num for num in nums]
heapq.heapify(nums)
for _ in range(k):
res = -heapq.heappop(nums)
return res
(2)347. 前 K 个高频元素
使用 Counter 统计每个元素的频率
使用最小堆,维护一个大小为 k 的频率
heapq.heappush(heap, (count, num))
- 往
heap
中加入元素(count, num)
- 最小堆会自动首先
按照第一个元素 count 大小排序
,构建最小堆
import heapq
from collections import Counter
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
cnt = Counter(nums) # 统计词频
heap = []
for num, count in cnt.items():
heapq.heappush(heap, (count, num))
if len(heap) > k:
heapq.heappop(heap)
return [num for count, num in heap]