堆排序——基本概念及基本实现代码
堆的基本概念
(二叉)堆是一个数组,它可以被看成一个近似的完全二叉树,树上每个节点对应数组里的一个元素。除了最底层外,该树是完全充满的,且是从左向右填充的。
假设A是表示堆的数组,则A包含两个属性:
(1)A.length : 数组的元素个数;
(2)A.heap_size : 数组中有多少个元素用来存储堆里的元素。
这里,A.heap_size <= A.length.
二叉堆的根结点是A[1],对于一个给定的下标i(i>1),其父节点、左孩子节点、右孩子节点的下标为:
PARENT(i) = i / 2;
LEFT(i) = i2;
RIGHT(i) = i2 + 1.
例如下图,下标为5的节点,其父节点下标为5 / 2 = 2,其左孩子节点下标为5*2 = 10.
堆通常可以分为最小堆和最大堆:
(1)最大堆:除了根节点外,其余节点满足:A[PARENT(i)] >= A[i].
简单来说,就是该堆里的每个节点,其父节点一定大于或等于它的值,该堆的最大元素放在了根节点中。
(1)最小堆:除了根节点外,其余节点满足:A[PARENT(i)] <= A[i].
简单来说,就是该堆里的每个节点,其父节点一定小于或等于它的值,该堆的最小元素放在了根节点中。
堆的几个实现函数
1.维护堆的性质
MAX_HEAPIFY : 输入一个表示堆的数组A和一个下标,假定根节点为LEFT(i)和RIGHT(i)的二叉树都是最大堆,这时A[i]可能小于它的孩子,则通过该函数来维护最大堆。
void MAX_HEAPLFY(int* A, int i) {
int l, r, m, temp;
l = 2*i;
r = 2*i +1;
m = i;
temp = A[i];
if(l<=heap_size && A[i]<A[l]) {
temp = A[l];
m = l;
}
if(r<=heap_size && A[r]>A[m]) {
temp = A[r];
m = r;
}
if(m!=i) {
A[m] = A[i];
A[i] = temp;
MAX_HEAPLFY(A, m);
}
}
2.建堆
BULD_MAX_HEAP:输入一个数组A,将该数组转换为一个最大堆
void BULD_MAX_HEAP(int* A, int length) {
heap_size = length;
int i;
for(i = heap_size/2; i>0; i--) {
MAX_HEAPLFY(A, i);
}
}
3.堆排序算法
HEAPSORT: 输入一个数组A,并对其进行基于堆的排序
BULD_MAX_HEAP(A, length);
int temp, i;
for(i=length; i>1; i--) {
temp = A[1];
A[1] = A[i];
A[i] = temp;
heap_size--;
MAX_HEAPLFY(A, 1);
}
}
优先队列
优先队列是一种用来维护有一组元素构成的集合S的数据结构,其中每个元素都有一个关键字(key)。一个最大优先队列支持以下操作:
INSERT(S, x) : 把元素插入集合S中;
HEAP_MAXIMUM(S):返回S中关键字最大的元素;
EXTRACT_MAX(S):去掉并返回S中具有最大关键字的元素;
INCREASE_KEY(S, id, add_value):将下标为id的元素的关键字加上add_value,并调整S,让其仍是一个最大优先队列。
HEAP_MAXIMUM(S)
int HEAP_MAXIMUM(int* A) {
return A[1];
}
EXTRACT_MAX(A)
int EXTRACT_MAX(int* A) {
//if(heap_size<1)
int m, i;
m = A[1];
A[1] = A[heap_size];
heap_size--;
MAX_HEAPLFY(A, 1);
return m;
}
INCREASE_KEY(S, id, add_value)
void INCREASE_KEY(int* A, int i, int add_number) {
A[i] = A[i] + add_number;
int parent, temp;
while(i>1) {
parent = i/2;
if(A[parent] > A[i]) {
break;
}
temp = A[parent];
A[parent] = A[i];
A[i] = temp;
i = parent;
}
}