不知不觉,基本排序算法写到4了,应该是比较法排序的最后一篇,选择排序和冒泡排序就不单独总结了。
堆排序,似乎放在数据结构里面总结更好一些,因为涉及到堆这种数据结构来管理算法执行中的信息,堆排序达到了比较排序的时间下限O(NlogN),空间复杂度只有O(1).
堆是一个必须掌握的数据结构,一般用数组来表示,表示堆的数组A是一个具有两个属性的对象,length[A]表示数组中的元素个数,即数组的长度,heap-size[A]表示数组中堆的元素个数,亦即,A[heap-size[A]]之后的元素都不属于堆。
树的根为A[1],给定某个节点的下表i,就能计算出父节点,左孩子,右孩子的下标:
PARENT(i)=i/2;
LEFT(i)=2i;
RIGHT(i)=2i+!;
如果是大根堆,即对任意元素A[i],有A[PARENT(i)]>=A[i];
堆有四个基本操作,最重要的是Max_Heapify,用来保持大根堆性质的关键。(小根堆相同)
Max_Heapify过程如下所述,已知i所指向元素的左右子树均符合大根堆的定义,但A[i]可能小于他的子女,如此图中所示:
此时应选择其左右子女中最大的一个节点,与i所指向的节点交换,i所指向的节点下移后,递归调用Max_Heapify继续比较其新的子女是否满足大根堆的条件。
整个过程c++代码如下:(重点)
void MaxHeapify(int *a,int index,int heapSize)
{
int lChildIndex=2*index;
int rChildIndex=2*index+1;
int temp,largest=index;
if(lChildIndex<=heapSize&&a[lChildIndex]>a[largest])
{
largest=lChildIndex;
}
if(rChildIndex<=heapSize&&a[rChildIndex]>a[largest])
{
largest=rChildIndex;
}
if(largest!=index)
{
temp=a[index];
a[index]=a[largest];
a[largest]=temp;
MaxHeapify(a,largest,heapSize);
}
}
有了这个函数,堆的其他操作都变得如此简单
在一个数组A[N]上建堆:
void BuildMaxHeap(int *a,int length)
{
for(int i=length/2;i>=0;i--)
MaxHeapify(a,i,length);
}
堆排序:
void HeapSort(int *a,int length)
{
int temp;
BuildMaxHeap(a,length);
for(int i=length-1;i>=0;i--)
{
temp=a[i];
a[i]=a[0];
a[0]=temp;
length--;
MaxHeapify(a,0,length);
}
}
还有一个经常用的操作,即返回根元素A[0],此时A[0]从堆中删去,要求A仍满足大根堆性质,请自行实现。