面试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 的最顶端。

相关推荐
文丑颜不良啊2 小时前
LeetCode2221~2240题目解析
java·leetcode
秋饼2 小时前
Computer Use Agent Java 落地实战:感知-行动循环、执行器与安全围栏生产级实现
java·ai·技术分享·后端开发
Navigator_Z2 小时前
LeetCode //C - 1317. Convert Integer to the Sum of Two No-Zero Integers
c语言·算法·leetcode
wo_jack2 小时前
C++ 入门笔记(十六):第16课 vector 模拟实现十问十答 —— 从三指针到迭代器失效
c++·后端·面试·stl·vector
文丑颜不良啊4 小时前
LeetCode2161~2180题目解析
java·leetcode
聪明的笨猪猪5 小时前
AI应用架构选型:架构师的核心决策困境与权衡
经验分享·面试·架构
文丑颜不良啊6 小时前
LeetCode2141~2160题目解析
java·leetcode
LabVIEW开发6 小时前
LabVIEW 连接板:28 个端子怎么排,优先级怎么定
java·算法·labview
天天被压力6 小时前
【A股分钟级高频数据完全手册 #07】北交所历史分钟K线接口:京市Pro版分时批量
java·人工智能·python
天天被压力7 小时前
【A股分钟级高频数据完全手册 #08】股票日、分时数据接口对比:普通分时与Pro版差异实测
java·人工智能·python