您的位置:首页 > 其它

堆排序算法

2017-05-16 16:10 127 查看
堆排序(heapsort),在最坏情形和平均情形下,时间复杂度都为O(nlgn),具有空间原址性,只需常数个额外的元素空间存储临时数据。

顾名思义,堆排序采用了“堆”这一数据结构,下面先简单介绍一下堆的知识。(参考算法导论第三版内容)

(二叉)堆在内存中是一个数组的形式,可以看作一个完全二叉树,从左向右填充。n个结点的堆,高度为θ(lgn).

设定树的根结点是A[0],这样给定一个下标i,它的父结点、左孩子、右孩子的下标关系为:

父结点PARENT(i): return (i-1)/2

左孩子LEFT(i): return 2i+1

右孩子RIGHT(i): return 2i+2



左图为其存储结构,右图为其逻辑结构

二叉堆分为最大堆最小堆。堆排序算法中使用最大堆,最小堆通常用于构造优先队列。

最大堆的两个特性:

1.父结点的键值总是大于或等于两个子结点的键值。

2.每个结点的左子树和右子树都是一个最大堆(二叉堆)。

算法1:构建一个维护最大堆性质的函数MaxHeapify

a[i]可能小于其孩子,这样就违背了最大堆性质,通过让a[i]的值在最大堆中“逐级下降”,使得以下标i为根结点的子树重新遵循最大堆的性质。

//定义两个有参宏来寻找数组中的数之间的关系
#define left(x) 2*x+1;//获得左结点在数组中的下标
#define right(x) 2*x+2;//获得右结点在数组中的下标
//假设某一个结点i的左右子树都是最大堆,但是对于结点i和其左右子结点可能破坏最大堆性质
void MaxHeapify(int* a, int i, int low, int high){//输入为要被排序的数组和根结点,数组a当中被维护的那一部分的下标low,high
int l=left(i);//i的左子结点
int r=right(i);//i的右子结点
int largest;//保存i,l,r(即i和它的左右子结点)之间的最大数的下标
int temp;
//找到三个数当中最大的那个数,将最大的那个数和i进行互换
if(l<=hith && a[l]>a[i])
largert=l;
else largest=i;
if(r<=high && a[r]>a[largert])
largest=r;
if(largest!=i){
temp=a[i];
a[i]=a[largest];
a[largest]=temp;
MaxHeapify(a, largest, low, high);
//交换有可能破坏子树的最大堆性质,所以对所交换的那个子结点进行一次维护,
//而未交换的那个子结点,根据我们的假设,是保持着最大堆性质的。
}
}


算法2:建堆函数BuildMaxHeap

这个函数负责将数组中元素的位置进行更改保证数组能够构造成为一个最大堆,实现如下:

//将数组建立为一个最大堆
//调整数组当中数的位置将其处理为一个最大堆
void BuildMaxHeap(int* a, int length){
for(int i=length/2-1; i>=0; i--)
MaxHeapify(a, i, 0, length-1)
}


i=length/2-1,这也是因为数组的起始下标为0.从最后的一个非叶结点开始“从小到大”地进行维护,保证每一个点的子树都是最大堆,可以确保最大堆的性质。

算法3:堆排序函数HeapSort

void HeapSort(int a[], int length){
int temp;
BuildMaxHeap(a,length);
for(int i=length-1; i>=1; i--){
//交换根结点和数组的最后一个结点
temp=a[i];
a[i]=a[0];
a[0]=temp;
MaxHeapify(a, 0, 0, i-1);//维护从下标为i-1到0的子数组
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  堆排序 算法