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

堆排序是借助于一种称为堆的完全二叉树结构进行排序的,
排序过程中,将向量中存储的数据看成是一棵完全二叉树的顺序存储结构,
利用完全二叉树中的父结点和孩子结点之间的内在关系来选择关键字最小的记录。


实现方法:
a)把待排序的记录存放在数组r[1‥n]中,将r看作一棵二叉树,每个结点表示一个记录,
b)第一个记录r[1]作为二叉树的根,以后各记录r[ 2 ],…,r[n]依次逐层从左到右顺序排列,构成一棵完全二叉树,任意结点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为需要排序的元素个数。

它是一种不稳定的排序方法。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值