堆排序

堆排序利用了大根堆的两个特点:

1.      堆顶元素是最大元素;

2.      后一次堆调整可以在前一次的基础上进行;

堆排序利用了数组的一个特点:

1.      快速定位指定索引的元素;

使用了以上的特点使得对无序数组进行排序变得简单而高效。

用大根堆排序的基本思想

1.      先将初始文件R[0..n-1]建成一个大根堆,此堆为初始的无序区

2.      再将关键字最大的记录R[0](即堆顶)和无序区的最后一个记录R[n-1]交换,由此得到新的无序区R[0..n-2]和有序区R[n-1],且满足R[0..n-2].keys≤R[n-1].key

3.      由于交换后新的根R[0]可能违反堆性质,故应将当前无序区R[0..n-2]调整为堆。然后再次将R[0..n-2]中关键字最大的记录R[0]和该区间的最后一个记录R[n-2]交换,由此得到新的无序区R[0..n-3]和有序区R[n-2..n-1],且仍满足关系R[1..n-3].keys≤R[n-2..n-1].keys,同样要将R[0..n-3]调整为堆。

大根堆排序算法的基本操作:

1.      建堆,建堆是不断调整堆的过程,从len/2-1处开始调整,一直到根结点,此处len是堆中元素的个数。建堆的过程是线性的过程,从len/2-10处一直调用调整堆的过程,相当于o(h1)+o(h2)…+o(hlen/2)其中h表示结点的深度,len/2表示结点的个数,这是一个求和的过程,结果是线性的O(n)

2.      调整堆:调整堆在构建堆的过程中会用到,而且在堆排序过程中也会用到。利用的思想是比较结点i和它的孩子结点left(i),right(i),选出三者最大者,如果最大值不是结点i而是它的一个孩子结点,那边交互结点i和该结点,然后再调用调整堆过程,这是一个递归的过程。调整堆的过程时间复杂度与堆的深度有关系,是lgn的操作,因为是沿着深度方向进行调整的。

3.      堆排序:堆排序是利用上面的两个过程来进行的。首先是根据元素构建堆。然后将堆的根结点取出(一般是与最后一个结点进行交换),将前面len-1个结点继续进行堆调整的过程,然后再将根结点取出,这样一直到所有结点都取出。堆排序过程的时间复杂度是O(nlgn)。因为建堆的时间复杂度是O(n)(调用一次);调整堆的时间复杂度是lgn,调用了n-1次,所以堆排序的时间复杂度是O(nlgn)。由于建堆只调用了一次,当n很大,即元素个数较多时,建堆过程的时间消耗可以忽略不计。如果n值比较小,则建堆过程的时间消耗不能忽略。

具体的代码

 

#include <stdio.h>
#define N 10
void heap_build(int array[], int length);
void heap_adjust(int array[], int i, int length);
void heap_sort(int array[], int length);
int main(void)
{
	int buffer[N] = {1,2,3,4,5,6,7,8,9,0};
	int i;
	
	/*build up heap*/
	heap_build(buffer, N);
	for(i = 0; i < N; i++)
	{
		printf("%3d", buffer[i]);
	}
	printf("\nbuild heap  successfully!\n");
	
	/*heap sort*/
	heap_sort(buffer,N);
	for(i = 0; i< N; i++)
	{
		printf("%3d", buffer[i]);
	}
	printf("\nheap sort successfully!\n");
}
void heap_build(int array[], int length)
{
	int i;
	/*调整第一个非叶子结点至根结点*/
	for (i = length / 2 - 1; i >= 0; i--)
	{
		heap_adjust(array, i, length);
	}
}
void heap_adjust(int array[], int i, int length)
{
	int lchild, rchild, max;
	int temp;
	while(i < length -1)
	{
		lchild = 2*i + 1;
		rchild = 2*i + 2;
		max = i;
		if (lchild < length)
		{
			if (array[max] < array[lchild])
			{
				max = lchild;
			}
		}
		else			/*该结点不存在叶子结点,即该结点本身就是叶子结点*/
		{
			break;
		}
		if (rchild < length)
		{
			if (array[max] < array[rchild])
			{
				max = rchild;
			}
		}
		if (max != i)	/*该结点小于其子结点*/
		{
			/*交换,使该结点下移一层*/
			temp = array[i];
			array[i] = array[max];
			array[max] = temp;
			/*继续调整子结点,直至满足大根堆条件*/
			i = max;
		}
		else			/*该结点满足大根堆条件,不需调整*/
		{
			break;
		}
	}
}
void heap_sort(int array[], int length)
{
	int i;
	int temp;
	for (i = length - 1; i > 0; i--)
	{
		/*交换根结点和最后一个结点*/
		temp = array[i];
		array[i] = array[0];
		array[0] = temp;
		/*在剩余无序区域中,继续调整根结点使之成为大根堆*/
		heap_adjust(array, 0, i);
	}
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值