思路
有n个元素的序列r[1..n]
满足第k个元素不小于2k与2k+1
或者不大于2k与2k+1。
很明显,堆可以看成完全二叉树的线性序列
由以上性质可知,堆-二叉树的树根节点 就是序列中最大或者最小的元素(r[1])
那么我们把一个序列变成堆,就可以得到最值关键字。根选择排序思想一致,找出最大或最小关键字。
步骤:
序列r[1..n] 变成堆,假设是大根堆,交换r[1]与r[n];
序列r[1..n-1]变成堆,交换r[1]与r[n-1];
重复上述步骤,直到交换r[1]与r[2] ,即n-1次变成堆的操作。
最终变成一个增序序列。
如何把无序序列变成堆?
调整堆
如果有r[s+1..m]为堆,r[s..m]不为堆
那么就要把r[s+1..m]变成以r[s]为根节点的堆r[s..m]
算法步骤:
从r[2s]与r[2s+1]中选择关键字较大者(大根堆),假设r[2s]较大
比较r[s]与r[2s];
r[s]>=r[2s] ,不需要调整
r[s]<r[2s],交换两者。因为以r[2s+1]为根的子树仍是堆,故不需要调整。
令s=2s,重复上述过程,直到r[s]为根节点。
代码如下:
struct Sqlist {
int r[100];
int len;
};
void HeapAdjust(Sqlist& L,int s,int m)
{
int rc = L.r[s];
for (int j = 2 * s; j <= m; j *= 2)
{
if (j < m && L.r[j] < L.r[j + 1])++j;//较大关键字
if (rc >=L.r[j])break;//不需要调整
L.r[s] = L.r[j]; s = j;//r[s]替换
}
L.r[s] = rc;//叶节点的值更新为rc
}
建初堆
首先思考在完全二叉树中最小堆是?
很明显是叶节点。那么以叶节点为孩子节点的最小子树,只有根节点不是满足堆性质的。
算法步骤:
若序列有n个节点,那么第[n/2]个节点就是最后一个非终端节点(非叶节点)。从[n/2]个节点开始,[n/2]-1,[n/2]-2,,,1.依次调整堆,最后整个序列都被调整为堆
代码如下:
void Creatheap(Sqlist& L)
{
int n = L.len;
for (int i = n / 2; i > 0; i--)
{
HeapAdjust(L, i, n);
}
}
堆排序代码:
void HeapSort(Sqlist& L)
{
Creatheap(L);
for (int i = L.len; i > 1; i--)
{
int x = L.r[1];
L.r[1] = L.r[i];
L.r[i] = x;
HeapAdjust(L, 1, i - 1);
}
}
实验结果:
感谢您的观看,后续会出归并排序,希尔,快速排序。