mirror of
https://github.com/krahets/hello-algo.git
synced 2026-08-17 06:00:58 +00:00
build
This commit is contained in:
@@ -4,17 +4,19 @@ comments: true
|
||||
|
||||
# 8.2 建堆操作
|
||||
|
||||
如果我们想要根据输入列表生成一个堆,这个过程被称为「建堆」。
|
||||
在某些情况下,我们希望使用一个列表的所有元素来构建一个堆,这个过程被称为「建堆」。
|
||||
|
||||
## 8.2.1 借助入堆方法实现
|
||||
|
||||
最直接的方法是借助“元素入堆操作”实现,首先创建一个空堆,然后将列表元素依次添加到堆中。
|
||||
最直接的方法是借助“元素入堆操作”实现。我们首先创建一个空堆,然后将列表元素依次执行“入堆”。
|
||||
|
||||
设元素数量为 $n$ ,则最后一个元素入堆的时间复杂度为 $O(\log n)$ 。在依次添加元素时,堆的平均长度为 $\frac{n}{2}$ ,因此该方法的总体时间复杂度为 $O(n \log n)$ 。
|
||||
设元素数量为 $n$ ,入堆操作使用 $O(\log{n})$ 时间,因此将所有元素入堆的时间复杂度为 $O(n \log n)$ 。
|
||||
|
||||
## 8.2.2 基于堆化操作实现
|
||||
|
||||
有趣的是,存在一种更高效的建堆方法,其时间复杂度仅为 $O(n)$ 。我们先将列表所有元素原封不动添加到堆中,**然后迭代地对各个节点执行“从顶至底堆化”**。当然,**我们不需要对叶节点执行堆化操作**,因为它们没有子节点。
|
||||
有趣的是,存在一种更高效的建堆方法,其时间复杂度可以达到 $O(n)$ 。我们先将列表所有元素原封不动添加到堆中,然后倒序遍历该堆,依次对每个节点执行“从顶至底堆化”。
|
||||
|
||||
请注意,因为叶节点没有子节点,所以无需堆化。在代码实现中,我们从最后一个节点的父节点开始进行堆化。
|
||||
|
||||
=== "Java"
|
||||
|
||||
@@ -195,7 +197,7 @@ comments: true
|
||||
|
||||
为什么第二种建堆方法的时间复杂度是 $O(n)$ ?我们来展开推算一下。
|
||||
|
||||
- 完全二叉树中,设节点总数为 $n$ ,则叶节点数量为 $(n + 1) / 2$ ,其中 $/$ 为向下整除。因此,在排除叶节点后,需要堆化的节点数量为 $(n - 1)/2$ ,复杂度为 $O(n)$ 。
|
||||
- 在完全二叉树中,设节点总数为 $n$ ,则叶节点数量为 $(n + 1) / 2$ ,其中 $/$ 为向下整除。因此,在排除叶节点后,需要堆化的节点数量为 $(n - 1)/2$ ,复杂度为 $O(n)$ 。
|
||||
- 在从顶至底堆化的过程中,每个节点最多堆化到叶节点,因此最大迭代次数为二叉树高度 $O(\log n)$ 。
|
||||
|
||||
将上述两者相乘,可得到建堆过程的时间复杂度为 $O(n \log n)$ 。**然而,这个估算结果并不准确,因为我们没有考虑到二叉树底层节点数量远多于顶层节点的特性**。
|
||||
@@ -221,7 +223,7 @@ T(h) & = 2^0h + 2^1(h-1) + 2^2(h-2) + \cdots + 2^{h-1}\times1 \newline
|
||||
\end{aligned}
|
||||
$$
|
||||
|
||||
**使用错位相减法**,令下式 $2 T(h)$ 减去上式 $T(h)$ ,可得
|
||||
使用错位相减法,用下式 $2 T(h)$ 减去上式 $T(h)$ ,可得
|
||||
|
||||
$$
|
||||
2T(h) - T(h) = T(h) = -2^0h + 2^1 + 2^2 + \cdots + 2^{h-1} + 2^h
|
||||
|
||||
@@ -324,7 +324,7 @@ comments: true
|
||||
|
||||
下文实现的是大顶堆。若要将其转换为小顶堆,只需将所有大小逻辑判断取逆(例如,将 $\geq$ 替换为 $\leq$ )。感兴趣的读者可以自行实现。
|
||||
|
||||
### 堆的存储与表示
|
||||
### 1. 堆的存储与表示
|
||||
|
||||
我们在二叉树章节中学习到,完全二叉树非常适合用数组来表示。由于堆正是一种完全二叉树,**我们将采用数组来存储堆**。
|
||||
|
||||
@@ -565,7 +565,7 @@ comments: true
|
||||
}
|
||||
```
|
||||
|
||||
### 访问堆顶元素
|
||||
### 2. 访问堆顶元素
|
||||
|
||||
堆顶元素即为二叉树的根节点,也就是列表的首个元素。
|
||||
|
||||
@@ -676,7 +676,7 @@ comments: true
|
||||
}
|
||||
```
|
||||
|
||||
### 元素入堆
|
||||
### 3. 元素入堆
|
||||
|
||||
给定元素 `val` ,我们首先将其添加到堆底。添加之后,由于 val 可能大于堆中其他元素,堆的成立条件可能已被破坏。因此,**需要修复从插入节点到根节点的路径上的各个节点**,这个操作被称为「堆化 Heapify」。
|
||||
|
||||
@@ -1049,7 +1049,7 @@ comments: true
|
||||
}
|
||||
```
|
||||
|
||||
### 堆顶元素出堆
|
||||
### 4. 堆顶元素出堆
|
||||
|
||||
堆顶元素是二叉树的根节点,即列表首元素。如果我们直接从列表中删除首元素,那么二叉树中所有节点的索引都会发生变化,这将使得后续使用堆化修复变得困难。为了尽量减少元素索引的变动,我们采取以下操作步骤:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user