笔记
本节主要给出(二叉)堆的定义。堆是一种用数组保存的完全二叉树。完全二叉树的意思是,除了树的最底层,其他层都完全充满的。树中的元素按照从上到下、从左到右的顺序保存在数组中。下图给出了堆的一个例子,左图是完全二叉树的形式,右图是堆在数组中的表示。
如果用
A
A
A表示堆所在的数组,那么
A
.
l
e
n
g
t
h
A.length
A.length表示数组的容量,即数组最多可容纳的元素的个数;
A
.
h
e
a
p
_
s
i
z
e
A.heap\_size
A.heap_size表示堆中的元素个数。显然有
A
.
h
e
a
p
_
s
i
z
e
≤
A
.
l
e
n
g
t
h
A.heap\_size ≤ A.length
A.heap_size≤A.length。树的根结点是元素
A
[
1
]
A[1]
A[1]。
给定一个结点的下标
i
i
i,根据下式可以得到它的父结点、左孩子和右孩子的下标:
以上3个公式成立的前提是数组下标从1开始,这是《算法导论》这部书的约定。我们写C/C++代码时,数组下标是从0开始的。在这种情况下,上面3个公式应当变为
二叉堆分两种:最大堆和最小堆。在最大堆中,除了根以外的所有结点
i
i
i都要满足以下性质。即对于任意一棵子树,子树的根结点一定是子树的最大元素。
相反地,在最小堆中,除了根以外的所有结点
i
i
i都要满足以下性质。即对于任意一棵子树,子树的根结点一定是子树的最小元素。
练习
6.1-1 在高度为
h
h
h的堆中,元素个数最多和最少分别是多少?
解
高度为
h
h
h的堆一共有
h
+
1
h+1
h+1层。第一层有
1
1
1个元素,第二层有
2
2
2个元素,第三层有
4
4
4个元素,以此类推。除最后一层外,第
i
i
i层有
2
i
−
1
2^{i-1}
2i−1个元素。
最后一层为第
h
+
1
h+1
h+1层,最少有
1
1
1个元素,最多有
2
h
2^h
2h个元素。因此,高度为
h
h
h的堆中,最少元素个数为
∑
i
=
1
h
2
i
−
1
+
1
=
2
h
\sum_{i=1}^h{2^{i-1}}+1 = 2^h
∑i=1h2i−1+1=2h;而最多元素个数为
∑
i
=
1
h
+
1
2
i
−
1
=
2
h
+
1
−
1
\sum_{i=1}^{h+1}2^{i-1} =2^{h+1}-1
∑i=1h+12i−1=2h+1−1。
6.1-2 证明:含
n
n
n个元素的堆的高度为
⌊
l
g
n
⌋
⌊{\rm lg}n⌋
⌊lgn⌋。
解
利用练习6.1-1的结论,高度为
h
h
h的堆中元素最少有
2
h
2^h
2h个,最多有
2
h
+
1
−
1
2^{h+1}-1
2h+1−1个,即
2
h
≤
n
≤
2
h
+
1
−
1
2^h ≤ n ≤ 2^{h+1}-1
2h≤n≤2h+1−1。在
n
n
n的这个取值范围内,必然有
h
=
⌊
l
g
n
⌋
h=⌊{\rm lg}n⌋
h=⌊lgn⌋。
6.1-3 证明:在最大堆的任一子树中,该子树所包含的最大元素在该子树的根结点上。
略
6.1-4 假设一个最大堆的所有元素都不相同,那么该堆的最小元素应该位于哪里?
解
最小元素可以位于任意一个叶子结点上。
6.1-5 一个已排好序的数组是一个最小堆吗?
解
如果一个数组已按升序排好序,那么它就是一个最小堆。如果数组按降序排序,那么它是一个最大堆。
6.1-6 值为<23, 17, 14, 6, 13, 10, 1, 5, 7, 12>的数组是一个最大堆吗?
解
如果画出二叉树,我们会发它并不是一个最大堆。
6.1-7 证明:当用数组表示存储
n
n
n个元素的堆时,叶结点下标分别为
⌊
n
/
2
⌋
+
1
,
⌊
n
/
2
⌋
+
2
,
…
,
n
⌊n/2⌋+1,⌊n/2⌋+2,…,n
⌊n/2⌋+1,⌊n/2⌋+2,…,n。
解
考虑最后一个结点,其下标为
n
n
n,它的父结点
⌊
n
/
2
⌋
⌊n/2⌋
⌊n/2⌋必然是最后一个非叶结点,而之后的结点必然都为叶结点。所以叶结点下标为
⌊
n
/
2
⌋
+
1
,
⌊
n
/
2
⌋
+
2
,
…
,
n
⌊n/2⌋+1,⌊n/2⌋+2,…,n
⌊n/2⌋+1,⌊n/2⌋+2,…,n。