mirror of
https://github.com/krahets/hello-algo.git
synced 2026-08-13 04:10:58 +00:00
Mention figures and tables in normal texts.
Fix some figures. Finetune texts.
This commit is contained in:
@@ -95,11 +95,11 @@
|
||||
|
||||
将上述两者相乘,可得到建堆过程的时间复杂度为 $O(n \log n)$ 。**然而,这个估算结果并不准确,因为我们没有考虑到二叉树底层节点数量远多于顶层节点的特性**。
|
||||
|
||||
接下来我们来进行更为详细的计算。为了减小计算难度,我们假设树是一个“完美二叉树”,该假设不会影响计算结果的正确性。设二叉树(即堆)节点数量为 $n$ ,树高度为 $h$ 。上文提到,**节点堆化最大迭代次数等于该节点到叶节点的距离,而该距离正是“节点高度”**。
|
||||
接下来我们来进行更为详细的计算。为了减小计算难度,我们假设树是一个“完美二叉树”,该假设不会影响计算结果的正确性。设二叉树(即堆)节点数量为 $n$ ,树高度为 $h$ 。
|
||||
|
||||

|
||||
|
||||
因此,我们可以将各层的“节点数量 $\times$ 节点高度”求和,**从而得到所有节点的堆化迭代次数的总和**。
|
||||
如上图所示,**节点“从顶至底堆化”的最大迭代次数等于该节点到叶节点的距离,而该距离正是“节点高度”**。因此,我们可以将各层的“节点数量 $\times$ 节点高度”求和,**从而得到所有节点的堆化迭代次数的总和**。
|
||||
|
||||
$$
|
||||
T(h) = 2^0h + 2^1(h-1) + 2^2(h-2) + \dots + 2^{(h-1)}\times1
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 堆
|
||||
|
||||
「堆 heap」是一种满足特定条件的完全二叉树,可分为两种类型:
|
||||
「堆 heap」是一种满足特定条件的完全二叉树,主要可分为下图所示的两种类型:
|
||||
|
||||
- 「大顶堆 max heap」:任意节点的值 $\geq$ 其子节点的值。
|
||||
- 「小顶堆 min heap」:任意节点的值 $\leq$ 其子节点的值。
|
||||
@@ -321,7 +321,7 @@
|
||||
|
||||
当使用数组表示二叉树时,元素代表节点值,索引代表节点在二叉树中的位置。**节点指针通过索引映射公式来实现**。
|
||||
|
||||
具体而言,给定索引 $i$ ,其左子节点索引为 $2i + 1$ ,右子节点索引为 $2i + 2$ ,父节点索引为 $(i - 1) / 2$(向下取整)。当索引越界时,表示空节点或节点不存在。
|
||||
如下图所示,给定索引 $i$ ,其左子节点索引为 $2i + 1$ ,右子节点索引为 $2i + 2$ ,父节点索引为 $(i - 1) / 2$(向下取整)。当索引越界时,表示空节点或节点不存在。
|
||||
|
||||

|
||||
|
||||
@@ -527,7 +527,7 @@
|
||||
|
||||
给定元素 `val` ,我们首先将其添加到堆底。添加之后,由于 val 可能大于堆中其他元素,堆的成立条件可能已被破坏。因此,**需要修复从插入节点到根节点的路径上的各个节点**,这个操作被称为「堆化 heapify」。
|
||||
|
||||
考虑从入堆节点开始,**从底至顶执行堆化**。具体来说,我们比较插入节点与其父节点的值,如果插入节点更大,则将它们交换。然后继续执行此操作,从底至顶修复堆中的各个节点,直至越过根节点或遇到无须交换的节点时结束。
|
||||
考虑从入堆节点开始,**从底至顶执行堆化**。如下图所示,我们比较插入节点与其父节点的值,如果插入节点更大,则将它们交换。然后继续执行此操作,从底至顶修复堆中的各个节点,直至越过根节点或遇到无须交换的节点时结束。
|
||||
|
||||
=== "<1>"
|
||||

|
||||
@@ -662,7 +662,7 @@
|
||||
2. 交换完成后,将堆底从列表中删除(注意,由于已经交换,实际上删除的是原来的堆顶元素)。
|
||||
3. 从根节点开始,**从顶至底执行堆化**。
|
||||
|
||||
顾名思义,**从顶至底堆化的操作方向与从底至顶堆化相反**,我们将根节点的值与其两个子节点的值进行比较,将最大的子节点与根节点交换;然后循环执行此操作,直到越过叶节点或遇到无须交换的节点时结束。
|
||||
如下图所示,**“从顶至底堆化”的操作方向与“从底至顶堆化”相反**,我们将根节点的值与其两个子节点的值进行比较,将最大的子节点与根节点交换。然后循环执行此操作,直到越过叶节点或遇到无须交换的节点时结束。
|
||||
|
||||
=== "<1>"
|
||||

|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
|
||||
## 方法一:遍历选择
|
||||
|
||||
我们可以进行 $k$ 轮遍历,分别在每轮中提取第 $1$ , $2$ , $\dots$ , $k$ 大的元素,时间复杂度为 $O(nk)$ 。
|
||||
我们可以进行下图所示的 $k$ 轮遍历,分别在每轮中提取第 $1$ , $2$ , $\dots$ , $k$ 大的元素,时间复杂度为 $O(nk)$ 。
|
||||
|
||||
该方法只适用于 $k \ll n$ 的情况,因为当 $k$ 与 $n$ 比较接近时,其时间复杂度趋向于 $O(n^2)$ ,非常耗时。
|
||||
此方法只适用于 $k \ll n$ 的情况,因为当 $k$ 与 $n$ 比较接近时,其时间复杂度趋向于 $O(n^2)$ ,非常耗时。
|
||||
|
||||

|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
## 方法二:排序
|
||||
|
||||
我们可以对数组 `nums` 进行排序,并返回最右边的 $k$ 个元素,时间复杂度为 $O(n \log n)$ 。
|
||||
如下图所示,我们可以先对数组 `nums` 进行排序,再返回最右边的 $k$ 个元素,时间复杂度为 $O(n \log n)$ 。
|
||||
|
||||
显然,该方法“超额”完成任务了,因为我们只需要找出最大的 $k$ 个元素即可,而不需要排序其他元素。
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
## 方法三:堆
|
||||
|
||||
我们可以基于堆更加高效地解决 Top-K 问题,流程如下:
|
||||
我们可以基于堆更加高效地解决 Top-K 问题,流程如下图所示。
|
||||
|
||||
1. 初始化一个小顶堆,其堆顶元素最小。
|
||||
2. 先将数组的前 $k$ 个元素依次入堆。
|
||||
|
||||
Reference in New Issue
Block a user