Heap:贪心算法助手
Greedy Gas Fill
Alice和Bob要去roadtrip,问题来了:因为路途有点远,我们估计要在路上停下来加油。那么去哪里加油能让我们停下次数最少呢?不同的加油站离出发距离不一样,能加油的量也不一样。就有了这样的对话:
Bob提议:“别一边开一边计划了,我们就一脚油门开到底,不行了再“后悔“去错过的那些能加最多油的地方加。“
Alice说:“那你怎么知道哪些地方油最多呢?“
Bob回答道:“我们在路上拿个小本本记下来就是了“。
这个“小本本“,把油最多的地方记下来的数据结构,就可以是堆(Heap)。
输入:target = 100, startFuel = 10, stations = [[10,60],[20,30],[30,30],[60,40]]
输出:2
解释:
我们出发时有 10 升燃料。
我们开车来到距起点 10 英里处的加油站,消耗 10 升燃料。将汽油从 0 升加到 60 升。
然后,我们从 10 英里处的加油站开到 60 英里处的加油站(消耗 50 升燃料),
并将汽油从 10 升加到 50 升。然后我们开车抵达目的地。
我们沿途在1两个加油站停靠,所以返回 2 。因为python默认最小对,所以要反转一下符号:
def minRefuelStops(target, startFuel, stations):
far = stop = 0
queue = []
stations.append((target, float('inf')))
for dist, fuel in stations:
startFuel -= (dist-far) # 一脚油门开到这儿
while startFuel < 0 and queue:# 不行,得去小本本上油最多的地方加油
startFuel -= heapq.heappop(queue)
stop += 1
if startFuel < 0: return -1
heapq.heappush(queue, -fuel) # 拿小本本记下最多可以加多少油
far = dist
return stop二叉堆
堆Heap,又叫优先队列Priority Queue。本质是一棵满二叉树。它把数组中d的d[0]空出来,然后每一个数据就很方便的能找到它的左右儿子d[left]=d[i*2],d[right]=d[i*2+1]。一个最小堆的重要性质是每个节点都小于等于它的子节点。

注意Python中默认的heapq永远是最小堆,如果要把它当作最大堆来用,就在heappush的时候加入val的负值-val。别在heappop的时候忘记把负号反过来就好了:)
heapq库中除了常规的heappush,heappop的操作,还几个方便的函数,可以让你少写几行代码:
heappushpop:和先push再pop等价heapreplace:和先pop再push等价,不过更高效一些。
时间复杂度上,heappush和heappop都是O(logn)的复杂度,然而原地建成一个堆的heapify是O(n)的操作(粗略证明)。
还有一些其他的堆类型,在其他操作时有更快的时间效率(Wikipedia,OI Wiki配对堆等介绍)。
数组简单实现
下面是手动实现的最小堆。最大堆只需把less函数变成more就好了。
主要功能:
insert(val):O(logn)插入一个元素 实现:在满二叉树的最后插入一个元素,然后用swim(k)让它游上来pop():O(logn)得到并删除最小元素 实现:得到满二叉树的根值,交换根与尾元素交换然后删除新的尾元素,然后让新的根用sink(k)沉下去
swim(k):不断地把第k个元素val和父亲比较,如果val小就游上去sink(k):不断地把第k个元素val和小儿子比较,如果val大就沉下去
经典的例题有:
进程安排:最小堆保存了最早结束时间
最大的K个数:大小为K的最小堆作为缓存,来筛选是否加入新元素
例题:进程安排
下面几题都用优先队列来帮助找到最先结束的进程。
例题:最大的K个数字
例题:最大平均通过率
例题:车队 II
Last updated
Was this helpful?