这个数据结构建立在满二叉树 (full binary tree)和完全二叉树 (complete binary tree)的概念上.
"满" 这个字眼提示在树的每一层都摆满了节点, 而这恰好又是个充要条件, 即如果一棵二叉树每一层都堆满了节点, 那么它就是满二叉树. 满二叉树的定义干脆就按节点个数来: 一棵二叉树如果深度为 K, 而拥有 2K-1 个节点, 那么它就是一棵满二叉树. 如下面是 2 层和 3 层满二叉树, 分别拥有 3 个和 7 个节点
+---+
| a |
+---+ +---+
| a | .---' `---.
+---+ +---+ +---+
/ \ | b | | c |
+---+ +---+ +---+ +---+
| b | | c | / \ / \
+---+ +---+ +---+ +---+ +---+ +---+
| d | | e | | f | | g |
+---+ +---+ +---+ +---+
- 除了最后一层, 其余层构成一棵满二叉树
- 最后一层从右起缺少 0 个或多个连续的节点
+---+
| 1 |
+---+
.-----------' `-----------.
+---+ +---+
| 2 | | 3 |
+---+ +---+
.---' `---. .---' `---.
+---+ +---+ +---+ +---+
| 4 | | 5 | | 6 | | 7 |
+---+ +---+ +---+ +---+
/ \ / \ / \ / \
+---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+
| 8 | | 9 | | A | | B | | C | | D | | E | | F |
+---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+
+---+
| 1 |
+---+
.-----------' `-----------.
+---+ +---+
| 2 | | 3 |
+---+ +---+
.---' `---. .---' `---.
+---+ +---+ +---+ +---+
| 4 | | 5 | | 6 | | 7 |
+---+ +---+ +---+ +---+
/ \ / \ /
+---+ +---+ +---+ +---+ +---+
| 8 | | 9 | | A | | B | | C |
+---+ +---+ +---+ +---+ +---+
+---+ -.
| 1 | |
+---+ |
.-----------' `-----------. |
+---+ +---+ |
| 2 | | 3 | | 非满二叉树
+---+ +---+ |
.---' `---. .---' |
+---+ +---+ +---+ |
| 4 | | 5 | | 6 | |
+---+ +---+ +---+ -'
/ \ / \ / \
+---+ +---+ +---+ +---+ +---+ +---+
| 8 | | 9 | | A | | B | | C | | D |
+---+ +---+ +---+ +---+ +---+ +---+
+---+
| 1 |
+---+
.-----------' `-----------.
+---+ +---+
| 2 | | 3 |
+---+ +---+
.---' `---. .---' `---.
+---+ +---+ +---+ +---+
| 4 | | 5 | | 6 | | 7 |
+---+ +---+ +---+ +---+
/ \ / \ / /
+---+ +---+ +---+ +---+ +---+ +---+ -.
| 8 | | 9 | | A | | B | | C | | E | | 缺少的节点不连续
+---+ +---+ +---+ +---+ +---+ +---+ -'
如果完全二叉树上, 非叶节点元素的值小于或等于其子节点元素的值, 那么称它为最小堆 (minimum heap), 相反, 如果非叶节点元素的值大于或等于其子节点元素的值, 那么称这棵完全二叉树为最大堆 (maximum heap). 在计算机科学领域, 动态分配的内存所在的内存区域也被称之为堆 (heap), 然而这两个是完全不同的概念. 这里要讨论的堆是数据结构, 跟内存分配没有关系. 下面是一个整数最小堆的示例
+---+
| 2 |
+---+
.----------' `----------.
+---+ +----+
| 4 | | 12 |
+---+ +----+
.---' `---. / \
+---+ +---+ +----+ +----+
| 5 | | 5 | | 13 | | 11 |
+---+ +---+ +----+ +----+
/ \ / \ /
+---+ +---+ +---+ +---+ +----+
| 8 | | 7 | | 7 | | 6 | | 14 |
+---+ +---+ +---+ +---+ +----+
+---+
| 2 |
+---+
.----------' `----------.
+---+ +----+
| 4 | | 12 |
+---+ +----+
.---' `---. / \
+---+ +---+ +----+ +----+
| 5 | | 5 | | 13 | | 11 |
+---+ +---+ +----+ +----+
/ \ / \ / \
+---+ +---+ +---+ +---+ +----+ =====
| 8 | | 7 | | 7 | | 6 | | 14 | [ 9 ]
+---+ +---+ +---+ +---+ +----+ =====
+---+
| 2 |
+---+
.----------' `---------.
+---+ +----+
| 4 | | 12 |
+---+ +----+
.---' `---. / \
+---+ +---+ ===== +----+
| 5 | | 5 | [ 9 ] | 11 |
+---+ +---+ ===== +----+
/ \ / \ / \
+---+ +---+ +---+ +---+ +----+ +----+
| 8 | | 7 | | 7 | | 6 | | 14 | | 13 |
+---+ +---+ +---+ +---+ +----+ +----+
+---+
| 2 |
+---+
.----------' `----------.
+---+ +---+
| 4 | | 9 |
+---+ +---+
.---' `---. / \
+---+ +---+ +----+ +----+
| 5 | | 5 | | 12 | | 11 |
+---+ +---+ +----+ +----+
/ \ / \ / \
+---+ +---+ +---+ +---+ +----+ +----+
| 8 | | 7 | | 7 | | 6 | | 14 | | 13 |
+---+ +---+ +---+ +---+ +----+ +----+
那么, 最小堆的插入 - 修正过程可以简述为
- 将新节点插入完全二叉树中下一个子节点位置;
- 如果新节点不是根节点, 且新节点的值小于其父节点的值, 则交换它们.
而从堆中弹出最小值节点, 也就是根节点. 与插入类似, 要让根节点弹出后, 满足二叉树的性质, 便于以后修正堆的性质. 要做到这一点, 须将根节点与最后一个子节点交换, 再移除之, 就不会破坏完全二叉树结构了.
======
[ 13 ] <-------------------.
====== |
.----------' `---------. |
+---+ +---+ |
| 4 | | 9 | |
+---+ +---+ |
.---' `---. / \ |
+---+ +---+ +----+ +----+ |
| 5 | | 5 | | 13 | | 11 | |
+---+ +---+ +----+ +----+ |
/ \ / \ / |
+---+ +---+ +---+ +---+ +----+ - - |
| 8 | | 7 | | 7 | | 6 | | 14 | : 2 : <-----'
+---+ +---+ +---+ +---+ +----+ - -
+---+
| 4 |
+---+
.----------' `---------.
====== +---+
[ 13 ] | 9 |
====== +---+
.---' `--. / \
+---+ +---+ +----+ +----+
| 5 | | 5 | | 13 | | 11 |
+---+ +---+ +----+ +----+
/ \ / \ /
+---+ +---+ +---+ +---+ +----+
| 8 | | 7 | | 7 | | 6 | | 14 |
+---+ +---+ +---+ +---+ +----+
+---+
| 4 |
+---+
.----------' `-----------.
+---+ +---+
| 5 | | 9 |
+---+ +---+
.--' `----. / \
====== +---+ +----+ +----+
[ 13 ] | 5 | | 13 | | 11 |
====== +---+ +----+ +----+
/ \ / \ /
+---+ +---+ +---+ +---+ +----+
| 8 | | 7 | | 7 | | 6 | | 14 |
+---+ +---+ +---+ +---+ +----+
+---+
| 4 |
+---+
.-----------' `----------.
+---+ +---+
| 5 | | 9 |
+---+ +---+
.---' `----. / \
+---+ +---+ +----+ +----+
| 7 | | 5 | | 13 | | 11 |
+---+ +---+ +----+ +----+
/ \ / \ / \
+---+ +----+ +---+ +---+ +----+ +----+
| 8 | | 13 | | 7 | | 6 | | 14 | | 13 |
+---+ +----+ +---+ +---+ +----+ +----+
堆的移除 - 修正过程简单来说就是
- 用完全二叉树编号最大的节点替换根节点;
- 让当前的根节点与其子节点中较小的进行交换, 直到完全二叉树成为堆.