堆排序

在堆排序之前,首先得了解几个东西。

首先是满二叉树:一个二叉树所有的分支节点都存在左子树与右子树,而且所有的叶子节点都在同一层上,则这个二叉树就是满二叉树。

然后是完全二叉树:对一颗二叉树进行层序编号,如果编号为i的节点与同样深度的满二叉树中编号为i的节点的位置完全相同,则这个二叉树就是完全二叉树。

第一个就是完全二叉树的性质。为了说明完全二叉的性质的时候,这里只写出层序符号。下面就是一颗完全二叉树:

                                        1

                           2                                3 

                  4                5               6                7

             8         9      10        11      12             
n个节点的完全二叉树,深度为[log n ] +1,按层编号后,查看上面这个完全二叉树,得出每个节点的编号i有这几条性质:

1、当i=1的时候,节点i是二叉树的根,没有双亲节点,如果i>1,则双亲节点是[ i/2 ]。

2、如果2i>n,则节点i无左子树,否则只能是这个节点的左子树为2i。

3、如果2i+1>n,则节点i无右孩子,否则只能是这个节点的右子树为2i+1。

有了上面的概念后,下面来看堆的概念。


所谓的堆就是一种完全二叉树,每个节点的值都大于等于左右子树节点的值,称为大顶堆,或者每个节点小于等于左右子树节点的值,称为小顶堆。

所以节点值满足:K[i]>=K[2i+1] && K[i]>=K[2i] 或者 K[i]<=K[2i+1] && K[i]<=K[2i]。这里的i的取值为1到[n/2]。

接下来是堆排序的思想:

待排序的数组元素我们以大顶堆的方式存入,排序的时候,最大元素就是根节点,将根节点与末尾元素进行互换,并将末尾元素移走,得到元素中最大值,然后调整剩下n-1个节点,重新调整为大顶堆,继续与末尾元素互换,再移出。一直这样执行,最后获得的序列就是有序的。

现在问题来了。。。就是怎么构造一个堆呢?

这里我们构造大顶堆,故而每个节点i满足K[i]>=K[2i+1] && K[i]>=K[2i] 。下面查看从begin起始到end结束的大顶堆构建,这里需要注意的,我们严格按照数组下标的方式进行(0,1,2,3,4......),故而左右节点的坐标分别为2i+1,2i+2。

void heapcreat(int arr[],int begin,int end)
{
	int temp,j;

	temp=arr[begin];
	for(j=2*begin+1;j<=end;j=j*2)
	{
		if(j<end && arr[j] < arr[j+1])
			++j;
		if(temp>=arr[j])
			break;
		arr[begin]=arr[j];
		begin=j;
	}
	arr[begin]=temp;
}

begin的下一层,如果未超出end的范围,则需要找出下一层中最大的值(也就是1处的代码),并且记住标记j,比较处的值与temp大小。两种情况进行处理:

1、如果比j处的值大,则满足大顶堆的条件,退出循环,此处的值并不进行交换。

2、如果比j处的值小,则将这一层找出的最大值赋值给begin处元素,然后新的起始点开始于这层的最大值j,循环找到满足条件的j后,进行交换。

为了便于理解,参考下面的图:



知道上面怎么调整一个大顶堆,下面我们来看堆的排序,分为堆的创建与堆的排序。代码如下:

void heapsort(int arr[],int length)
{
	int i;
	int end;

	end = length-1;

	for(i=end/2;i>=0;i--)
		heapcreat(arr,i,end);
	for(i=end;i>0;i--)
	{
		swapdata(arr,0,i);
		heapcreat(arr,0,i-1);
	}
}
堆的创建主要是第一个for循环,由于heapcreat步长每次增加两倍,则这里从end/2开始,完成堆的创建。

然后第二个for循环完成堆的排序,构建完成大顶堆后,根元素,也就是i=0的时候,是最大元素,将此元素交换到数组的末位。然后再调整0~i-1,使其成为大顶堆(其实这里只需要一次调整即可完成),再交换,再调整。。。最后完成堆的排序。

完整的测试代码如下:

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

#define ARR_LEN 1000


void swapdata(int arr[],int i,int j)
{
	int temp;

	temp=arr[i];
	arr[i]=arr[j];
	arr[j]=temp;
}

void print_arr(int arr[],double time,int length)
{
	int i;
	for(i=0;i<length;i++)
		printf("%d\t",arr[i]);
	printf("\nrunning time:%.10lf s\n",time);
}
void heapcreat(int arr[],int begin,int end)
{
	int temp,j;

	temp=arr[begin];
	for(j=2*begin+1;j<=end;j=j*2)
	{
		if(j<end && arr[j] < arr[j+1])
			++j;
		if(temp>=arr[j])
			break;
		arr[begin]=arr[j];
		begin=j;
	}
	arr[begin]=temp;
}
void heapsort(int arr[],int length)
{
	int i;
	int end;

	end = length-1;

	for(i=end/2;i>=0;i--)
		heapcreat(arr,i,end);
	for(i=end;i>0;i--)
	{
		swapdata(arr,0,i);
		heapcreat(arr,0,i-1);
	}
}

int main()
{
	srand((unsigned int)time(NULL));
	int a[ARR_LEN],i;
    double  time=0;
	time_t begin,end;
	for(i=0;i<ARR_LEN;i++)
	{
		a[i]=rand()%1000;
	}
    begin = clock();
    heapsort(a,ARR_LEN);
	end = clock();
        time=double(end - begin)/CLOCKS_PER_SEC;
	print_arr(a,time,ARR_LEN);
}
这里对排序的时间复杂度为O(n*logn)


 


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值