面试Leetcode - Heap 堆

Heap

堆 = 一个能快速拿到"当前最小值/最大值"的特殊数据结构。

核心价值是动态维护一个集合中的最大/最小元素。

最常见的是两种:

  • Min Heap(小根堆):堆顶永远是最小值
  • Max Heap(大根堆):堆顶永远是最大值

比如:

复制代码
       1
      / \
     3   5
    / \
   7   8

这是一个小根堆,因为:

1 < 3, 5

3 < 7, 8

但是注意:堆不是完全排序的。

比如 [1, 3, 5, 7, 8] 并不意味着整个数组都排好序了。

它只保证:最小值在最上面。

所以如果你只想反复拿"最小的那个",堆特别方便。

储存

Heap 通常用一个数组来存。

例如:

复制代码
        2
       / \
      3   5
     / \
    7   8

存成:

heap = 2, 3, 5, 7, 8

操作

push

把一个元素放进 Heap。向上调整(sift up)

假设:heap = 2, 3, 5, 7

现在加入:1

最简单的做法:直接把 1 放到数组最后。

变成:2, 3, 5, 7, 1

对应:

2

/ \

3 5

/ \

7 1

这显然不是 Heap,因为:

1 < 3

1 < 2

所以我们需要把 1 往上移动。

Sift Up

新元素现在 index = 4

它的 parent:parent = (4 - 1) // 2 = 1

所以比较:heap4 = 1 heap1 = 3

因为:1 < 3

交换:2, 1, 5, 7, 3

现在 1 在 index 1。

继续找 parent:parent = (1 - 1) // 2 = 0

比较:heap0 = 2 heap1 = 1 1 < 2

交换:1, 2, 5, 7, 3

完成。

实现

python 复制代码
def push(self, x):
    self.heap.append(x)

    i = len(self.heap) - 1

    while i > 0:
        parent = (i - 1) // 2

        if self.heap[parent] <= self.heap[i]:
            break

        self.heap[parent], self.heap[i] = \
            self.heap[i], self.heap[parent]

        i = parent

把新元素放最后

↓

看看爸爸

↓

如果我比爸爸小

↓

和爸爸交换

↓

继续往上

↓

直到爸爸比我小

pop

把 Heap 顶部,也就是最小值拿走。向下调整(sift down)

假设:

1

/ \

3 5

/ \

7 8

数组:1, 3, 5, 7, 8

现在:pop()

我们要删除 1。

但这里不能直接 del heap0

因为这样后面的结构全部乱掉。

正确做法是:

  1. 把最后一个元素拿到顶部:
复制代码
        8
       / \
      3   5
     /
    7

数组:8, 3, 5, 7

然后把 8 往下沉。

Sift Down

现在 8 在 index 0。

它有两个孩子:

复制代码
left = 2 * i + 1     left = 1 → 3
right = 2 * i + 2         right = 2 → 5

我们应该找两个孩子里面更小的那个交换。

因为我们是 Min Heap。

所以:3 < 5

跟 3 换:3, 8, 5, 7

3

/ \

8 5

/

7

8 现在在 index 1。

继续:

left = 3 → 7

right = 4 → 不存在

所以和 7 比,7 < 8 → 交换

3, 7, 5, 8

完成。

实现

python 复制代码
def pop(self):
    ans = self.heap[0]

    # 最后一个元素放到顶部
    self.heap[0] = self.heap[-1]
    self.heap.pop()

    i = 0

    while True:
        left = 2 * i + 1
        right = 2 * i + 2

        smallest = i

        if left < len(self.heap) and \
           self.heap[left] < self.heap[smallest]:
            smallest = left

        if right < len(self.heap) and \
           self.heap[right] < self.heap[smallest]:
            smallest = right

        if smallest == i:
            break

        self.heap[i], self.heap[smallest] = \
            self.heap[smallest], self.heap[i]

        i = smallest

    return ans

刷题

你刷 Heap 题的时候,可以先问自己:

我要维护的是 Top K 最大,还是 Top K 最小?

然后:

目标 Heap
Top K 最大 Min Heap
Top K 最小 Max Heap
不断取最小 Min Heap
不断取最大 Max Heap

这个规律非常重要。

例如:

第 K 大不是用 Max Heap!

而是:Min Heap,大小 K

因为我们需要让第 K 大卡在 Heap 的最顶端。

相关推荐
ym hyd 11111 分钟前
慢性病精细化管理平台源码 Java+SpringBoot+Vue3 前后分离
java·vue.js·spring boot·毕设
程序猿乐锅16 分钟前
【黑马点评 | 第七篇】Redis 分布式锁的两种实现
java·数据库·redis·分布式·spring·缓存
SL_staff21 分钟前
JVS私有化交付为何敢承诺100%源码开放与无兜底风险?
java·低代码·全栈
需要82633 分钟前
MySQL MVCC 与事务隔离级别:从一条 update 看版本链
java·数据库·spring boot·mysql·spring cloud
Memory_荒年33 分钟前
订单超时未支付?从“定时扫库”一步步到最终形态
java·后端
多多爱学习42 分钟前
括号有几层?得看还有多少个左括号没闭合
c语言·c++·算法·面试
丑陋小蚊子1 小时前
同一家公司升过岗,简历别合成一段
面试·求职招聘·大学生·简历·简历下载
此时不提桶,更待何时1 小时前
05-04-B-HBase与时序库面试与生产事故实战
数据库·面试·hbase
hai_android2 小时前
LruCache 图片浏览器内存缓存
android·java·kotlin
玩美移动2 小时前
AI Skin Analysis API 技术解析:文件上传、异步任务与结果读取
java·人工智能·python