几种排序总结(上)——堆排序

堆排序

这几天看了算法导论的排序部分,作一下总结。

堆排序的优点

       1)最坏情况下o(nlgn)的时间复杂度

       2)就地排序,不用辅助数组

几种操作(以最大堆为例)

1.保持堆性质

        这是主要操作,对于节点A[i],前提是以LEFT(i)和RIGHT(i)为根的子树已经是一个排列好的堆,因此如果A[i]比他的子节点要小,对A[i]进行判断递归下沉。

        在最坏的情况下,对于一棵有n个节点的树,使根结点下沉至树的底部,需要比较lgn次,因此时间复杂度为o(lgn),若对应高度为h,则为o(h)

2.建堆

        对于堆中的所有叶子节点来说,他们的父节点可以使用保持堆性质进行建堆,因为叶子节点本身就可以看作一棵排列好的堆。

        此时叶子节点以及他们的父节点都已经满足堆特性,至底向上对每个剩下的非叶子节点逐一调用保持堆性质操作直至到达根结点,建堆完成。

       由于在一棵有n个节点的树中,高度为h至多有(n/2^(h+1))(取上界)个节点,对高度为h的节点使用保持堆特性算法的时间复杂度为o(h),因此有

       而且

          所以建堆的紧确时间复杂度为o(n)

3.堆排序

        使用堆进行排序,其实就在建好的堆中取出堆顶的元素,然后将堆中最后一个元素放入堆顶部,再调用保持堆特性操作得到。将堆中元素全部取出后,就可以得到有序的排列。

        时间复杂度分析,第一步首先建堆需要用时o(n),第二步对大小为n的堆,取出元素放入数组尾部用时o(1),重新进行保持堆特性为o(lgn),因此o(n)+o(nlgn),总体时间时间复杂度为o(nlgn)

         由于从堆顶取出元素后,会将堆中的最后一个元素放入堆顶,因此会打乱了原来数组中元素的顺序,所以堆排序是不稳定的。


4.优先级队列

        优先级队列建立在堆的基础上,主要有返回最大元素/返回最大元素并取出/将第i个元素优先级增长为key/将元素x插入到优先级队列这几个操作。

         对于返回最大元素,直接返回最大堆堆顶即可。对于返回并取出最大元素,在返回后将最后一个堆元素放入堆顶,使堆大小减一并调用保持堆特性操作即可。对于将i元素优先级增长为key,不断比较i元素与他的父节点如果大于父节点则交换即可。对于插入元素,可以使堆大小加一,令最后一个元素为无穷小,再调用增长key操作即可。这些操作都可以在o(lgn)时间内完成。


实现代码如下:

#include
#include
using namespace std;
int a[100];

//保持堆特性操作
void max_heapify(int *a, int i, int len){
	int l=2*i;
	int r=2*i+1;
	int largest=i;
	if(l<=len){
		if(a[l]>a[i])
		  largest=l;
		else
		  largest=i;
	}
	if(r<=len){
		if(a[r]>a[largest])
		  largest=r;
	}
	if(largest!=i){
		swap(a[i],a[largest]);
		max_heapify(a,largest,len);
	}
}
//建堆
void build_max_heap(int *a, int len){
	for(int i=len/2; i>=1; i--){
		max_heapify(a,i,len);
	}
}
//堆排序
void heapsort(int *a,int len){
	build_max_heap(a,len);
	int heapsize=len;
	for(int i=len; i>=2; i--){
		swap(a[1],a[len]);
		len--;
		max_heapify(a,1,len);
	}
}
//优先队列中返回最大元素
int heap_maximum(int *a){
	return a[1];
}
//优先队列中返回并去掉最大元素
int heap_extract_max(int *a,int &len){
	int max = a[1];
	a[1]=a[len];
	len--;
	max_heapify(a,1,len);
	return max;
}
//将第i个元素的值增加为key
void heap_increase_key(int *a,int i,int key){
	if(key>a[i]){
		a[i]=key;
		while(i>1&&a[i/2]


你可能感兴趣的:(算法)