堆排序的实现-C语言

 堆

堆是一个完全二叉树的数组对象。树每一层都是满的,最后一层可能除外(从一个节点的左子树开始填)。

给定节点 i ,可以很容易计算父节点和子节点的位置。

Parent(i) =floor(i/2)   :i/2再向下取整

LeftChild(i) =2*(i+1)-1   :因为i从0开始,这和c语言的数组从0开始相对应。可以用左移运算代替*,即LeftChild(i)=(i+1)<<1 - 1;

RightChild(i) = 2*(i+1)   :RightChild(i) = (i+1)<<1;

堆排序

堆排序的时间复杂度是O(nlgn),是比较有效率的一种。其使用的是最大堆。最大堆的意思是父节点的值>=孩子的值。那么第0个节点必定是该堆的最大值。所以,堆排序的思想就是每次循环把最大值移走,然后从剩下的节点重新建立最大堆。

第一步:建立堆的结构体。

typedef struct heap_t{
	int *arr;	       //point for an array to store heap value.
	int heapMaxIndex;	//heap element max index number
	int arrLength;	//array length of arr
	
}Heap;

其中arr指针指向的是存放堆数据的数组。

heapMaxIndex是数组最大的序号。如数组定义为a[10],那么heapMaxIndex的值应该为9.

 

第二步:保持堆的性质。

这一步是堆排序的基础。这里将功能写成一个函数名为void maxHeapify(Heap *hp, unsigned int nodei),这个函数用于让一个数组变成一个符合堆性质的数组。时间复杂度为O(h),h是堆所属二叉树树的高度=lgn(n是节点个数)。

思想是:从一个节点i,和他的孩子leftchild(i),rightChild(i)中找到最大的,然后其索引存放在largest中。如果i是最大的。那么i为根的子树已经是最大堆,程序结束。

否则i的某个子节点有最大元素,那么i的值和largest的值交换。下标为largest的节点在交换后作为父节点,那么他可能又违反堆性质,因此递归调用该函数。

 

void maxHeapify(Heap *hp, unsigned int nodei)
{
	unsigned int l = (nodei+1) << 1 - 1;	//left child = 2i-1, -1 ?:arr[0..n-1]
	unsigned int r = (nodei+1) << 1 ;	// right child = 2i
	unsigned int largest = 0;
	int heapMaxI = hp->heapMaxIndex;

	if(l <= heapMaxI && hp->arr[l] > hp->arr[nodei])
		largest = l ;
	else
		largest = nodei ;
	
	if(r <= heapMaxI && hp->arr[r] > hp->arr[largest])
		largest = r ;

	if(largest!=nodei)
	{	
		//exchange 
		int tmp ;
		tmp = hp->arr[largest];
		hp->arr[largest] = hp->arr[nodei];
		hp->arr[nodei] = tmp;
		
		maxHeapify(hp,largest);
	}else{
		return ;
	}
	
}



第三步 利用maxHeapify函数创建堆

对于1个个数为n的堆,从上面堆的图中可以分析得到,n/2-1之前的都是父节点。之后的都是叶子节点,我们只需要对父节点进行maxHeapify就可以了。

n/2可以用右移运算n>>1。

Heap *createHeap(int *arrp, int arrLength,Heap *heap)
{
 int i;
 heap->arr = arrp;
 heap->heapMaxIndex = arrLength-1;
 heap->arrLength = arrLength;

 //for an heap a[0..n-1]; a[(n/2)..n-1] all are leaves
 for(i = arrLength>>1-1; i >=0; i--) 
  maxHeapify(heap,i);
 return heap;
}


第四步:堆排序

设堆的数组为A[0..n-1],调用maxHeapify函数就可以得到最大值,然后将最大值和n-1互换,把堆的大小heapMaxIndex减1,再次调用maxHeapify,又得到最大值,存放在A[0],再和A[n-2]互换,把堆的大小再减一,这样循环下去,知道堆的大小为0。那么我们就得到了由小到大的排好序的数组。



 

void heapSort(Heap *hp)
{
	int tmp;
	int last;
	while(hp->heapMaxIndex>0)
	{
		last = hp->heapMaxIndex ;
		//exchange
		tmp = hp->arr[last];
		hp->arr[last] = hp->arr[0];
		hp->arr[0] = tmp;
		hp->heapMaxIndex -= 1;
		maxHeapify(hp,0);	//make heap from root node 0 to heapMaxIndex 
	}
	
}


 

测试堆排序

void printArr(int *p, int size)
{
	int i;
	for(i=0; i<size;i++)
	{
		printf("%d ",p[i]);
	}
}

int main()
{
	int a[]={15,25,32,23,1,-4,35,2,-85,42,0,12,26,56,45,12,145,17,25,21};
	printArr(a,20);
	printf("\n");

	Heap hpa,*phpa;
	phpa =  createHeap(a,20,&hpa);
	heapSort(phpa);

	printArr(a,20);
	putchar('\n');
	return 0;	
}


结果为:-85 -4 0 1 2 12 12 15 17 21 23 25 25 26 32 35 42 45 56 145

堆排序应用:

优先级队列,事件驱动模式响应队列等。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值