堆排序

1、堆

二叉堆数据结构可以被看作一颗完全二叉树。对堆中元素i有如下关系:

i/2为元素i的父节点,2i为元素i的左子节点,2i+1为元素i的右子节点。

最大堆:对每个元素i都有i小于等于其父节点,大于等于其子节点。根节点为最大值。

最小堆:对每个元素i都有i大于等于其父节点,小于等于其子节点。根节点为最小值。


2、堆排序

假设一个序列<a1,a2,...,an>是一个最大堆,我们可以知道a1为序列的最大值。假如交换a1和an后还能使序列<a1,...,an-1>变成一个最大堆,则可以利用最大堆的特性来进行序列的排序。因此能够使用堆排序的关键在于建立最大堆或最小堆。

 堆排序步骤: 

1、将序列<a1,a2,...,an>构造成最大堆;

2、交换堆顶元素a1和堆尾元素an,得到序列:<a1',a2',...,an-1',an'>,其中an'为序列中的最大元素

3、将<a1',a2',...,an-1'>视为新的序列,重复步骤1、2,每次能够得到一个现有序列中的最大值,可以知道当n-1次操作后新序列已经排好序。


最大堆的构造:

可以知道一个最大堆,其左右子节点也是最大堆。因此上面堆排序步骤的第三步中,新序列的左右子节点是最大堆。实际上新序列重新构造成最大堆只需要很小的调整。

1、最大堆的调整

假设一个序列<a1,a2,...,an>,已知对元素ai,有其左右子节点为最大堆,比较ai,ai/2和a1/2+1的大小,有三种情况:

当ai为最大值时,此时ai及其子节点已经是最大堆了。

当ai/2为最大值时,交换ai和ai/2,可以知道ai为ai下子节点,子子节点的最大值,新的元素ai/2'有其其左右子节点为最大堆,对ai/2'进行向i一样的操作,当执行到最底端的元素时,便构成了最大堆。

当ai/2+1为最大值时,与ai/2为最大值一样。

2、初始序列最大堆的构造

我们知道一颗元素为n的完全二叉树,对第n/2后的元素并没有子节点,即可以将n/2后的元素视为最大堆。则通过n/2到1的循环可以逐步从完全二叉树的底部用1中的方法构建最大堆。


3、堆排序算法(c语言)

void swap(int *a, int *b)
{
	int x = *a;
	*a = *b;
	*b = x;
}
void HeapAdjust(int a[], int i, int length)
{

	int left = 2*i;			//i的左节点
	int right = 2*i + 1;		//i的右节点
	int k = i;
	if(k > length/2)	//当i〉length/2时,i不存在左右节点
		return;
	if(a[left - 1] > a[k - 1])	//注意对堆的序列是从1开始,但c语言数值的初始序号为0,故堆序列对应数组序列需要减1
		k = left;
	if(right <= length && a[right - 1]> a[k - 1])	//i<length/2时,i节点可能不存在右节点
		k = right;
	if(k != i) {
		swap(&a[i - 1], &a[k - 1]);
		HeapAdjust(a,k,length);			//k为与i节点交换元素的子节点,递归调整k节点
	}
}

void HeapBuild(int a[], int length)
{
	int i;
	for(i = length/2; i >= 1; i--) {	//从length/2到1逐步调整数组可以得到最大堆。
		HeapAdjust(a, i, length);
	}
}

void HeapSort(int a[], int length)
{
	int i;
	HeapBuild(a, length);			//最大堆建立后,a[0]为最大值
	for(i = length; i > 1; i--) {		//length到1的循环,得到有序数值
		swap(&a[0], &a[i - 1]);
		HeapAdjust(a, 1, i - 1);
	}
}

4、时间复杂度

对一个n位数组,堆的调整最坏情况需要递归log(n)(以2为底)次,堆的建立需要调用n次堆调整,之后的堆排序还需要调用n次堆首元素和尾元素的交换以及堆调整,时间复杂度为nlog(n)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值