堆排序
堆排序是利用一种叫堆的数据结构进行排序的算法,时间复杂度为O(nlogn),空间复杂度为O(1)。
什么是堆?堆分为最大堆和最小堆。堆首先是一颗完全二叉树,最大堆满足父节点的值大于等于它的所有子节点的值,最小堆就不用多说了。
下面我只说利用最大堆对数组进行升序排序的情况。
堆排序中有一个很重要的函数,void maxHeadify(T *A, int i)。这个函数的前提是节点i的左右子树都已经是最大堆了,这个函数让使以节点i为根节点的数为最大堆。实现方法很简单,首先找到节点i以及它的两个子节点中最大的那个,然后把它和节点i交换,然后再对它递归调用函数maxHeadify。
排序时,首先构建最大堆。即把输入的数组A转化为最大堆。因为完全二叉树从第n/2+1—n都是叶子节点,而叶子节点可以看做是只包含一个元素的堆,这样就可以从后往前调用maxHeadify,这样就可以构建好最大堆。然后将数组的第一个元素与最后的元素交换,那么最大的那个数就已经到了它正确的位置上了。接着把最后的那个元素排除在最大堆之外,即最大堆的长度-1。然后调用maxHeadify(A, 0),再次使堆最大化。这样重复调用n-1次,排序就完成了。
下面是具体的实现:
#include <iostream>
#include <algorithm>
/*
**使以i为根的子树保持最大堆的性质
**此函数的前提是以i的左儿子和右儿子为根的子树都是最大堆
**n为数组元素个数
*/
template <typename T>
void maxHeadify(T* A, int n, int i)
{
int leftChild = (i << 1) + 1;
int rightChild = (i << 1) + 2;
int largest = i;
/* 从A[i]、A[leftChild]、A[rightChild]中找出最大值,下标保存在largest中 */
if(leftChild < n && A[leftChild] > A[largest])
largest = leftChild;
if(rightChild < n && A[rightChild] > A[largest])
largest = rightChild;
if(largest != i)
{
std::swap(A[largest], A[i]); //将最大值交换到根节点
maxHeadify(A, n, largest); //递归调用,使largest保持最大堆
}
}
template <typename T>
void heapSort(T* A, int n)
{
//自底向上构建最大堆
for(int i = (n - 1) / 2; i >= 0; --i)
maxHeadify(A, n, i);
//依次把根节点移动到数组的最后
for(int i = n - 1; i >= 1; --i)
{
std::swap(A[i], A[0]);
maxHeadify(A, i, 0);
}
}
int main()
{
int A[] = {4, 1, 3, 2, 16, 9, 10, 14, 8, 7};
heapSort(A, 10);
return 0;
}