(二叉)堆(heap)数据结构是一种数组对象,可以视作一颗完全二叉树,从该二叉树的根开始层次遍历这颗二叉树就可以得到其对应的数组。树的根节点为A[0],对于树中某个节点的坐标i,其左右孩子节点和父亲节点的坐标可以很方便的求得:
LEFT(i)=2*i+1; RIGHT(i)=2*i+2; PARENT(i)=i/2 .
有两种二叉堆:最大堆和最小堆。最大堆中,每个节点存储的数值都大于等于其左右两个孩子节点存储的数值,亦即A[i]>=A[LEFT[i]]&&A[i]>=A[RIGHT[i]]。最小堆则正好相反。本文以最大堆为例。
知道了最大堆的定义之后,就要在给定的任意数组上构建最大堆,然后利用这个最大堆进行升序排序:
(1) 构建最大堆:
首先我们定义一个方法Heapify(heap,i),如果以i的左右孩子节点为根的子树已经是最大堆,则该方法使得以i为根的子树也成为最大堆。其伪代码如下(摘自《算法导论》):
1
MAX-
HEAPIFY(A,i)
2
l=
LEFT(i)
3
r=
RIGHT(i)
4
target=
i;
5
if
(l<=A.heap_size&&A[l]>
A[i])
6
target=
l;
7
if
(
if
(r<=A.heap_size&&A[r]>
A[i]))
8
target=
r;
9
if
(target!=
i)
10
exchange(A[i],A[target])
11
MAX-HEAPIFY(A,target)//递归
我们比较当前节点i的数值和其左右子节点的数值,如果i的某个子节点的数值大于i的数值,则违反了最大堆的定义,所以我们需要交换这两个节点的位置。假设A[LEFT[i]]>A[RIGHT[i]]>A[i],则交换i节点与LEFT[i]节点。但是,被交换到左孩子节点的i节点可能会违反以左孩子结点为根的最大堆的定义,所以我们需要对这个最大堆递归调用MAX-HEAPIFY方法。
有了上面这个方法之后,我们就可以自底向上的调用MAX-HEAPIFY方法构建最大堆。因为A[A.length/2+1]及其之后的节点都是叶子节点,都可以看做只有一个节点的最大堆,所以我们可以从A[A.length/2]节点开始直到A[0]节点依次调用MAX-HEAPIFY方法,亦即从树的层次遍历的最后一个有孩子的节点开始,按照层次遍历的逆序调用MAX-HEAPIFY方法:
1
BUILD-MAX-
HEAP(A)
2
for
i=A.length/
2
downto
1
3
MAX-HEAPIFY(A,i)
(2) 堆排序
构造完最大堆之后,我们就可以利用其进行排序。因为最大堆只能保证A[0]存储的是当前堆中最大的元素,我们可以把A[0]与堆的最后一个元素互换,这样A[0]就排在了最后一个位置,也是正确的位置。这时最后一个位置已经不属于最大堆,所以A.heap_size要减一。互换到A[0]的元素可能会破坏最大堆的性质,我们可以调用MAX-HEAPIFY方法使之重新成为最大堆,然后将A[0]交换至当前堆的最后一个位置。依次递归。
1
HEAPSORT(A)
2
for
i=A.length downto
2
3
exchange(A[i],A[
0
])
4
--A.heap_size
//
堆的大小减少
5
MAX-HEAPIFY(A,
0
)
(3) 堆的JAVA实现
1
package
Algorithm;
2
3
import
java.util.*
;
4
/**
5
* 堆(Heap)
6
*
@author
Kemaswill
7
* 2012-10-5 Friday
8
*/
9
10
public
class
Heap {
11
12
public
static
int
[] data;
13
public
static
int
length;
14
public
static
int
heap_size;
15
16
public
Heap(){
17
this
.length=20
;
18
this
.heap_size=
length;
19
this
.data=
new
int
[length];
20
Random random=
new
Random(System.currentTimeMillis());
21
for
(
int
i=0;i
){
22
data[i]=Math.abs(random.nextInt()%50
);
23
}
//
for
24
}
25
26
public
Heap(
int
n){
27
this
.length=
n;
28
this
.heap_size=
length;
29
this
.data=
new
int
[length];
30
Random random=
new
Random(System.currentTimeMillis());
31
for
(
int
i=0;i
){
32
data[i]=Math.abs(random.nextInt()%50
);
33
}
//
for
34
}
35
36
public
static
void
max_heapify(Heap heap,
int
i){
37
if
(i<
heap.heap_size){
38
int
l=2*i+1
;
39
int
r=2*i+2
;
40
int
target=
i;
41
if
(l
heap.data[i]){
42
target=
l;
43
}
44
if
(r
heap.data[target]){
45
target=
r;
46
}
47
if
(target!=
i){
48
exchange(heap,i,target);
49
max_heapify(heap,target);
50
}
//
if
51
}
//
if
52
}
//
heapify
53
54
public
static
void
exchange(Heap heap,
int
x,
int
y){
55
int
tmp=
heap.data[x];
56
heap.data[x]=
heap.data[y];
57
heap.data[y]=
tmp;
58
}
59
60
public
static
void
build_heap(Heap heap){
61
//
对于所有非叶结点,依次调用max_heapify
62
for
(
int
i=heap.heap_size/2;i>=0;i--
){
63
max_heapify(heap,i);
64
}
65
}
66
67
public
static
void
heapsort(Heap heap){
68
69
for
(
int
i=heap.length-1;i>0;i--
){
70
exchange(heap,0
,i);
71
heap.heap_size--
;
72
max_heapify(heap,0
);
73
}
74
}
//
heapsotr
75
76
public
static
void
show_heap(Heap heap){
77
for
(
int
i=0;i<=(
int
)Math.log(heap.length)/Math.log(2)+2;i++
){
78
for
(
int
j=(
int
)Math.pow(2, i)-1;j
){
79
if
(j<
heap.length){
80
System.out.print(heap.data[j]+" "
);
81
}
82
else
break
;
83
}
84
System.out.println();
85
}
86
}
87
88
public
static
void
main(String[] args){
89
Heap heap=
new
Heap();
90
show_heap(heap);
91
build_heap(heap);
92
show_heap(heap);
93
heapsort(heap);
94
show_heap(heap);
95
96
}
//
main
97
98
}
参考文献:
[1] 《算法导论》 第6章 p73