两个概念
- 元组
- 模拟大顶堆
1046.最后一块石头的重量
class Solution:
def lastStoneWeight(self, stones: List[int]) -> int:
# 将所有数取反,模拟小顶堆
heap = [-stone for stone in stones]
# 对数组构造小顶堆
heapq.heapify(heap)
while len(heap)>1:
x = heapq.heappop(heap)
y = heapq.heappop(heap)
# 如果最重的两个大小不一样,那么将差值加入
if x!=y:
heapq.heappush(heap,x-y)
if heap:return -heap[0]
return 0
三个技巧
固定一个大小为K的大顶堆,新加入的数和堆顶的数作比较,将较大的一个移除,那么最后剩下的就是最小的k个数,其中堆顶的数就是第K个小的数
固定一个大小为K的小顶堆,最后一个出堆的数就是第K小的数
295.数据流的中位数
class MedianFinder:
def __init__(self):
self.count = 0
self.max_heap = []
self.min_heap = []
def addNum(self, num: int) -> None:
self.count +=1
# 将num压入最大堆,由于Python中只有小顶堆,因此用一个负号去模拟小顶堆,因此用元组的形式
heapq.heappush(self.max_heap,(-num,num))
_,max_heap_top = heapq.heappop(self.max_heap)
heapq.heappush(self.min_heap,max_heap_top)
if self.count&1:
min_heap_top = heapq.heappop(self.min_heap)
heapq.heappush(self.max_heap,(-min_heap_top,min_heap_top))
def findMedian(self) -> float:
if self.count&1:
# 0 是指大顶堆的第一个数 1 是元组中的正数
return self.max_heap[0][1]
else:
return (self.max_heap[0][1] + self.min_heap[0])/2
1439.有序矩阵中的第 k 个最小数组和
class Solution:
def kthSmallest(self, mat: List[List[int]], k: int) -> int:
h = []
# 先初始化矩阵每一行的第一个数之和 以及指针的初始位置
cur = ( sum(vec[0] for vec in mat), tuple( [0] * len(mat) ))
heapq.heappush(h,cur)
# 通过集合,排除重复使用
seen = set(cur)
for _ in range(k):
acc,pointers = heapq.heappop(h)
# 对于指针进行每个指针的遍历
for i,pointer in enumerate(pointers):
# 如果指正还没到最后一个位置
if pointer != len(mat[0])-1:
t = list(pointers)
t[i] = pointer +1
tt = tuple(t)
if tt not in seen:
seen.add(tt)
heapq.heappush(h, (acc+mat[i][pointer+1] - mat[i][pointer],tt) )
return acc
264.丑数 II
class Solution:
def nthUglyNumber(self, n: int) -> int:
nums = [2,3,5]
explored = {1}
pq = [1]
for i in range(1,n+1):
x = heapq.heappop(pq)
if i == n:
return x
for num in nums:
t = x *num
if t not in explored:
explored.add(t)
heapq.heappush(pq,t)
return -1
class Solution:
def nthUglyNumber(self, n: int) -> int:
ans = [0] * (n+1)
ans [1] = 1
i2 = 1
i3 = 1
i5 = 1
idx = 2
while idx<=n:
a = ans[i2]*2
b = ans[i3]*3
c = ans[i5]*5
m = min(a,b,c)
if m ==a :
i2 +=1
if m == b:
i3 +=1
if m ==c :
i5 +=1
ans[idx] = m
idx +=1
return ans[n]
871.最低加油次数
class Solution:
def minRefuelStops(self, target: int, startFuel: int, stations: List[List[int]]) -> int:
stations +=[(target,0)]
cur = startFuel
ans = 0
last = 0
h = []
# 该加油站距离起点的距离和该加油站有多少油
for i ,fuel in stations:
cur -= (i-last)# 从上一个加油站到这一个加油站距离多远
while cur<0 and h:
cur -= heapq.heappop(h) # 就应该在上一个加油站加油,因为模拟小顶堆,因此用-
ans+=1
if cur<0:
return -1
heappush(h,-fuel)
last = i
return ans
1642.可以到达的最远建筑
class Solution:
def furthestBuilding(self, heights: List[int], bricks: int, ladders: int) -> int:
h = []
for i in range(1,len(heights)):
diff = heights[i] - heights[i-1]
# 两层的差值小于0,则继续
if diff<=0:
continue
# 如果差值大于0,并且砖头不够,但是有一个梯子
if bricks <diff and ladders>0:
ladders -=1 # 用一把梯子
# 如果之前的最大差值大于先在的差值,那么就应该在之前用梯子,从而节约砖头
if h and -h[0]>diff:
# 先查看一下,满足了才pop出第一个
bricks -= heapq.heappop(h)
# 如果不满足上述条件,则这一次就是用梯子,
else:
continue
bricks -=diff
if bricks<0:
return i-1
heapq.heappush(h,-diff)
return len(heights) -1
总结
理解什么时候使用最大堆、什么时候使用最小堆
最大堆求解什么问题、最小堆求解什么问题
堆的中心:动态求极值
最大值(大顶堆),最小值(小顶堆),主要核心是动态,如果不是动态其实没必要使用堆
分析题目是动态是难点