堆排序

1堆的理解与作用

首先要明白堆是种树结构(完全二叉树或近似完全二叉树),但一般采用数组进行存储。堆排序主要用在数据量大,而且所需序列已经为堆的情况下,即之前的数据先组织成堆,但后面的操作如(插入,查找)就比较方便了。这种布局允许原地重新组织堆,从而不必在增加或删除元素时分配大量内存。

 百度知道中的一个例子,觉得不错,能够说明它的作用。假设某个序列有500个作业,可以按它们的优先级建成堆,但每次只处理优先级最高的,所以可以按堆操作依次取顶。并删除顶元素序列没处理完时又会加进来新作业要用堆操作插入上操作只有用堆才能实现某时间确定再会有新作业插进来便由堆排序次得排序序列对排序时间复杂度恒定0(n*log(n)),会像其排序样有出入且空间复杂度V(n),

总之堆在实际中还是比较有用的,所以要好好学习

——————————————————————————————————————————————————————————————————

存储结构

堆可以分要大小堆,面所谓的大堆就是:父结点的key>=孩子结点。而小堆则是父结点的key<=孩子结点。最大堆的顶元素为最大值,但排序后得到的是递增数组。而最小堆的顶元素为最小值,排序得到的数组为递减数组。大小堆要根据实现来用。先看一下它的存储结构:

本文根的节点号表示为0,从而第i个节点就对应数组的下标i。

			0
	1	 			2
3		4		5		6
由上面可以发现结点i的孩子的节点号为2*i+1,2*i+2;而结点i的父节点号为floor((i-1)/2),floor表示向下取整。

下面以大堆的情况进行分析

2堆的插入

注意:堆的插入是先将它放在数组的最后,然后再进行调整的。将这个节点沿着根的方向与父节点进行比较。对大堆情况,当该节点的key大于父节点时就与父节点交换(相等时不用交换),如此直到根节点为止。而小堆,当该节点的key小于父节点时就与父节点交换(相等时不用交换),如此直到根节点为止。
下面以int arr[10]={4,6,3,7}方式进行堆插入操作,假设一开始为空:则最终为:7634,还不是按序的。
#include "stdio.h"
#include<math.h>
/*
	往堆中插入数据
	数组之前是开辟好的,新加入节点在数组的下标为i
*/
void Insert_Big_Heap(int H[],int i)
{
	int par=(int)floor((double)(i-1)/2);//父节点下标,当i=0时,par就变成-1了
	//如果为par=(i-1)/2,则i=0时par变成0,从而在下面的while中就要加上&&i!=0;	
	int temp;
	if(i<=0)
		return;
	temp=H[i];
	while(par>=0)//直到根节点
	{
		/*大堆,大的上浮*/
		if(temp<=H[par])//与父节点比较
		{
			break;
		}	
		else//上浮
		{
			/*新的在上浮之前,父节点要让位,下沉*/
			H[i]=H[par];
			i=par;
			par=(int)floor((double)(i-1)/2);
			
		}		
	}
	/*最后把新节点加入*/
	H[i]=temp;
}
/*在堆中插入新数据val*/
void Insert_Big_Heap_Val(int H[],int i,int val)
{
	H[i]=val;
	Insert_Big_Heap(H,i);	
}

void main(){ int arr[10]={4,6,3,7};int i=0;for(i=1;i<4;i++)Insert_Big_Heap(arr,i);for(i=0;i<4;i++){printf("%d\n",arr[i]);}}
 
 
7
6
3
4



             Press any key to continue

3堆的删除元素操作

	删除操作只能删除H[0]。留下的这个位置由最后一个数据进行填充,其实就相当于把堆中的头和尾交换。然后再从根开始从上而下进行调整。调整操作:对大堆来说,先从它的左右孩子结点中找到最大的。若父结点比孩子节点中的最大还大,则说明不要调整了。否则将父结点与孩子节点的最大节点交换,直到堆末尾。而对于小堆来说:先在左右孩子结点中找最小的, 如果父结点比这个最小的子结点还小说明不需要调整了, 反之将父结点和它交换后再考虑后面的结点。

/*以节点i向下调整,使之符合堆结构。
n为堆中有效的个数(若根节点为0,则n为存储结构中最后而的下标加1,即个数)
*/
void Adj_Arr_to_Heap(int H[],int i,int n)
{
	int temp=H[i];
	int j=2*i+1;//左孩子
	while(j<n)
	{
		/*左右孩子下标差一,找最大的那个使之上浮*/
		if(j+1<n)//右孩子可能不存在
		{//存在的情况
			if(H[j+1]>H[j])
				j++;//此时j为最大的下标。				
		}
		if(H[j]<=temp)
			break;
		H[i]=H[j];
		i=j;
		j=2*i+1;		
	}
	H[i]=temp;	
}
/*删除操作,n为原来堆中的元素个数*/
void Del_ele_from_Heap(int H[],int n)
{
	int temp=H[0];
	H[0]=H[n-1];
	H[n-1]=temp;
	Adj_Arr_to_Heap(H,0,n-1);
}


void main()
{  
	int arr[10]={4,6,3,7};
	int i=0;
	for(i=1;i<4;i++)
		Insert_Big_Heap(arr,i);
	for(i=0;i<4;i++)
	{
		printf("%d\n",arr[i]);	
	}
	Del_ele_from_Heap(arr,4);
	printf("after del op\n");
	for(i=0;i<4;i++)
	{
		printf("%d\n",arr[i]);	
	}
}

7
6
3
4
after del op
6
4
3
7



             Press any key to continue

删除操作H最后的元素7就是操作的节点。

4堆的建立

一种建立方式就是前面用到的插入法,但它需要从头开始对数组进行遍历,然后再比较操作,它的时间复杂度为O(nlgn),而调整法,它的时间复杂度为O(n)。
即从下往上进行调整。建立小堆的图很直观。
/*插入法建立堆*/
void Creat_Heap_by_Insert(int H[],int n)
{
	int i;
	for(i=1;i<n;i++)
		Insert_Big_Heap(H,i);
}
/*调整法建立堆*/
void Creat_Heap_by_Adj(int H[],int n)
{
	int i;//i代表双亲,最后节点的下标为n-1,则i=(n-1-1)/2=n/2-1,向下取整。
	for(i=n/2-1;i>=0;i--)//i
		Adj_Arr_to_Heap(H,i,n);
}


void main()
{  
	int arr[10]={4,6,3,7};
	int i=0;
	/*for(i=1;i<4;i++)
		Insert_Big_Heap(arr,i);
		*/
	//Creat_Heap_by_Insert(arr,4);
	Creat_Heap_by_Adj(arr,4);
	for(i=0;i<4;i++)
	{
		printf("%d\n",arr[i]);	
	}
	Del_ele_from_Heap(arr,4);
	printf("after del op\n");
	for(i=0;i<4;i++)
	{
		printf("%d\n",arr[i]);	
	}
}
——————————————————————————
printf("A+Adj\n");int A[10]={9,12,17,30,50,20,60,65,4,19};
	Creat_Heap_by_Adj(A,10);
	for(i=0;i<10;i++)
	{
		printf("%d\n",A[i]);	
	}
A+Adj
65
50
60
30
49
20
17
12
4
9
             Press any key to continue
上图中建立大堆的结果

4堆排序

建立好大堆后,进行删除操作,并且每次调整,
/*升序堆排*/
void Heap_sort_asd(int H[],int n)
{
	int i;
	int temp;
	Creat_Heap_by_Adj(H,n);//建堆	
	for(i=0;i<n-1;i++)
	{
		temp=H[0];
		H[0]=H[n-1-i];
		H[n-1-i]=temp;	
		Adj_Arr_to_Heap(H,0,n-i-1);//调整,每次会少一个;
	}
	
}

void main()
{  
	int arr[10]={4,6,3,7};
	int A[10]={9,12,17,30,50,20,60,65,4,49};
	int i=0;
	Heap_sort_asd(A,10);
	for(i=0;i<10;i++)
	{
		printf("%d\n",A[i]);	
	}

最后的结果就是从小到大排序的。
4
9
12
17
20
30
49
50
60
65



             Press any key to continue

算法的复杂度许多书中都有分析。几个关于堆的性质:堆排序的时间效率与待排序数据序列的顺序无关;是选择排序的一种,属于不稳定排序;



转载请标明出处:http://blog.csdn.net/lin200753/article/details/27981617

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值