目标:将数组按从小到大排列
算法描述:堆排序中,将待排序数组看成一颗完全二叉树,待排序数存放在A[1...n],不是从A[0]开始,因为要利用完全二叉树中节点编号之间的关系:下标为i的节点的左右孩子节点下标为2i和2i+1。对于大根堆A[i]>=A[2i]且A[i]>=A[2i+1],最大元素存放在根节点中(对于小根堆A[i]<=A[2i]且A[i]<=A[2i+1],最小元素存放在根节点中)。堆排序中,首先需要建立初始堆,然后将堆顶元素A[1](n个元素中的最大值)与堆底元素A[n]交换,对剩下的A[1...n-1]进行调整,使之重新成为一个大根堆,再将堆顶元素A[1](n-1个元素中的最大值)与堆底元素A[n-1]交换,如此重复,直到堆中只剩下一个元素为止。
算法实现(已经过测试运行):
/*函数功能:堆排序
**函数参数:A为数组,len为堆节点个数,即数组A的长度减1
*/
void HeapSort(int A[], int len)
{
BuildMaxHeap(A, len); //初始建堆
for (int i = 0; i < 8; i++){ cout << A[i] << endl; }
for (int i = len; i>1; i--)
{
int tmp = A[i];
A[i] = A[1];
A[1] = tmp;
AdjustDown(A, 1, i - 1); //从A[1]开始向下调整,把剩余的i-1个元素整理为大根堆
}
}
/*函数功能:建立大根堆
*/
void BuildMaxHeap(int A[], int len)
{
for (int i = len / 2; i>0; i--) //从len/2开始调整小的大根堆,一直调整到A[1]
{
AdjustDown(A, i, len);
}
}
/*函数功能:向下调整,将下标为i的元素以及其所有子孙建立成一个大根堆
**函数参数:A为数组,i为待调整元素下标
*/
void AdjustDown(int A[], int i, int len)
{
A[0] = A[i]; //暂存
for (int j = 2 * i; j <= len;j*=2)
{
if (j<len && A[j]<A[j + 1])
j++; //j记录值较大的左右孩子的下标
if (A[0] >= A[j])
break; //父节点本来就比孩子节点值都大,不用调整
else
{
A[i] = A[j];
i = j;
}
}
A[i] = A[0]; //被筛选节点的值最终放置位置为i
}
算法性能
时间复杂度:最好情况,O(nlogn)
最坏情况,O(nlogn)
平均情况,O(nlogn)
初始建堆的时间复杂度:O(n)
空间复杂度:仅使用了常数个辅助单元,O(1)
稳定性:不稳定
拓展:
在1000W个数中找出最小的1000个数,建立1000个节点的大根堆。当第1001个数出现,首先与堆顶(最大元素)比较,如果比堆顶还大,则第1001个数必然不在最小的1000个数之列,丢弃之;如果比堆顶小,则替换堆顶元素,并向下调整,使之重新成为一个大根堆。直到处理完1000W个数。
同理,如果要在1000W个数中找出最大的1000个数,则需要建立1000个节点的小根堆。