【数据结构】堆排序

一、堆

堆是具有下列性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。在堆排序中使用到大顶堆

完全二叉树的性质
●如果按照层序遍历的方式给结点从0开始编号,则结点之间满足如下关系:
●如果i = 0;则结点i是二叉树的根,无双亲
●如果i > 0; 对于左孩子,则其双亲是结点i/2;对于右孩子,则其双亲是结点(i/2)-1;那么对于有n个结点的二叉树而言,非叶子结点的i值(也就是由左右孩子的结点)小于等于i/2。

 

二、堆排序

堆排序的过程:
<1>构建大顶堆(自下而上调整):从非结点开始,比较左右孩子的大小,获得值比较大的孩子结点和父结点做比较,如果父结点大于此孩子结点,则进入下一次循环,否则交换父结点和孩子结点的值。
<2>得到序列的最大值:将大顶堆的根节点(最大值)与堆中的最后一个结点进行交换,下一次循环中,丢弃最后一个结点(i--),也就得到序列的最大值。
<3>再次调整成为大顶堆。

完整代码:

#include <stdio.h>
void Swap(int arr[],int a,int b)
{
	int temp;
	temp = arr[a];
	arr[a] = arr[b];
	arr[b] = temp;
}
void HeapSort(int arr[],int i,int len)
{
	int j = 2*i + 1;//i是一个子树中的根节点,j是子树中的左孩子
	for(j; j < len;j = i*2 +1)
	{
		if(arr[j+1] > arr[j] && j < len -1)
		{
			j++;//j指向孩子值比较大的一端
		}
		if(arr[i] >= arr[j])
		{
			break;
		}
		Swap(arr,i,j);
		i = j;
	}
}


void Heap(int arr[],int len)
{
	int i = len/2 - 1;//对于n个结点,非叶子结点的i值为n/2,这里的len是长度,所以要减一
	for(i;i >= 0;i--)
	{
		HeapSort(arr,i,len);//构建大顶堆,要自下而上调整;
	}

	for(int i = len -1;i >=0;i--)//i--,每次丢弃最后一个最大值元素,也就是每次循环得到一个最大值
	{
		Swap(arr,0,i);//将堆顶的最大值与最后一个元素交换
		HeapSort(arr,0,i);//将交换后的数据重新调整为大顶堆,因为下标为0的位置改变,所以每次从0号元素自顶向下开始调整;
	}
}
void Show(int arr[],int len)
{
	for(int i = 0;i < len;i++)
	{
		printf("%d ",arr[i]);
	}
}

int main()
{
	int arr[] = {8,39,4,809,89,1};
	int len = sizeof(arr)/sizeof(arr[0]);
	Heap(arr,len);
	Show(arr,len);
}

三、堆排序的时间复杂度

在构建堆的过程中,因为我们是完全二叉树从最下层最右边的非终端结点开始构建,将它与其孩子进行比较和若有必要的互换,对于每个非终端结点来说,其实最多进行两次比较和互换操作,因此整个构建堆的时间复杂度为O(n);

在正式排序时,第i次取堆顶记录重建堆需要用O(logi)的时间(完全二叉树的某个结点到根节点的距离为log2i + 1),并且需要取n - 1次堆顶记录,因此,重建堆的时间复杂度为O(nlogn)

所以总体来说,堆排序的时间复杂度为O(nlogn).

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值