您的位置:首页 > 其它

排序算法之堆排序(Heap Sort)

2012-11-08 22:26 204 查看
堆排序是借助于一种称为堆的完全二叉树结构进行排序的,

排序过程中,将向量中存储的数据看成是一棵完全二叉树的顺序存储结构,

利用完全二叉树中的父结点和孩子结点之间的内在关系来选择关键字最小的记录。

实现方法:

a)把待排序的记录存放在数组r[1‥n]中,将r看作一棵二叉树,每个结点表示一个记录,

b)第一个记录r[1]作为二叉树的根,以后各记录r[ 2 ],…,r
依次逐层从左到右顺序排列,构成一棵完全二叉树,任意结点r [i]的左孩子是r[2i],右孩子是r[2i+1],双亲是r[i/2]。 

对这棵完全二叉树的结点进行调整,使各结点的关键字满足下列条件:

                    r[i]≤r[2i]  且  r[i] ≤r[2i+1]

即每个结点的值均大于或小于它的两个子结点的值,称满足这个条件的完全二叉树为堆树。

显然在这个堆树中根结点的关键字最小,这种堆被称为“小根堆”,

各结点的值满足r[i]≥r[2i]并且r[i]≥r[2i+1]的堆,称为“大根堆”,大根堆中根结点的关键字值最大。

在堆中,根结点又被称为堆顶元素,当把二叉树转换成大根堆后,堆顶元素最大,把堆顶元素输出,重新调整二叉树的剩余结点,

使其成为一个新堆,再输出堆顶元素,便可求得次最大值,如此反复进行,就可得到一个有序序列,这就是利用大根堆排序的基本方法。

读者不难推导出小堆根的排序方法。 

完成堆排序必须解决两个问题:

(1) 如何将原始记录序列构造成一个堆,即建立初始堆。

(2) 输出堆顶元素后,如何将剩余记录调整成一个新堆。

因此,堆排序的关键是构造初始堆,其实构造初始堆的过程就是将待排元素序列对应的完全二叉树调整形成一个堆,

所以解决问题的关键在于如何调整元素间的关系使之形成初始堆。下面举例说明。 

建立初始堆如下图所示





堆排序过程如下:



代码实现如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX 10

void init_heap(int r[], int l, int m)
{
int i, j;
int x;
i = l; j = 2 * i;

x = r[i];

while(j <= m)
{
if(j < m && r[j] < r[j + 1])
j++;
if(x < r[j])
{
r[i] = r[j];
i = j;
j = 2 * i;
}
else
j = m + 1;
}

r[i] = x;
}

void heap_sort(int r[], int n)
{
int i, k;

int x;

for(i = n / 2; i > 0; i--)
init_heap(r, i, n);

for(i = n; i > 1; i--)
{
x = r[1];
r[1] = r[i];
r[i] = x;
init_heap(r, 1, i - 1);
}
}

void print_num(int r[])
{
int i;
for(i = 1; i < MAX + 1; i++)
{
printf("%d\n", r[i]);
}
}

int main(int argc, char** argv)
{
int num[MAX + 1] = {0, 36, 24, 48, 12, 65, 25, 43, 59, 76, 34};

printf("未排序的数据为:");
print_num(num);
printf("\n");

heap_sort(num, MAX);

printf("排序后的数据为:");
print_num(num);

system("pause");

return 0;
}

从上面介绍的堆排序算法可知,堆排序所需的比较次数是建立初始堆与重新建堆所需的比较次数之和,其平均时间复杂度和最坏的时间复杂度均为O(n logn),n为需要排序的元素个数。

它是一种不稳定的排序方法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: