一、堆排序算法原理
堆排序是利用堆的性质进行的一种选择排序。
堆实际上是一棵完全二叉树,其任何一非叶节点满足性质:
Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]或者Key[i]>=Key[2i+1]&&key>=key[2i+2]
即任何一非叶节点的关键字不大于或者不小于其左右孩子节点的关键字。
堆分为大顶堆和小顶堆,满足Key[i]>=Key[2i+1]&&key>=key[2i+2]称为大顶堆,满足Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]称为小顶堆。由上述性质可知小顶堆的堆顶的关键字肯定是所有关键字中最小的。下面讲述小顶堆排序过程。
给定一组数据量为n=6的集合Data={89 4 6 7 5}, ,使用小顶堆对其进行排序:
1、 首先由数据集Data建立一棵完全二叉树。
2、 调整树中节点,使其满足小顶堆结构,即Key[i]<=key[2i+1]&&Key[i]<=key[2i+2],调 整 过 程 :
从索引号i=[(n-1)/2]的节点进行操作,第一次i=2,即从4开始比较,由于4<5故不需要调整。向前走i=1,即对9所在节点比较,由于9>min(6,7)故需要调整。继续前行至i=0,即对8所在的根节点,由于8<min(6,4) 故需要调整。由于i=0,停止比较,至此建立好了一个初始化的最小顶堆结构。
3、 交换小堆顶元素Data[0]和Data[n-1],同时n=n-1;交换后由于Data[0]> min(Data[1], Data[2]),则此结构不满足小顶堆要求,执行2操作。
4、重复上述3操作,直到n=1。最终我们将Data 按降序排好。
上述小顶堆排序可用下面一组图直观表示出其排序过程:
注:蓝色圈为初始数组的完全二叉树结构;棕色为交换过的节点;绿色为初始小顶堆结构;红色为排序好的节点。
从上述知道,小顶堆每次操作完毕就会从未排序的数组中选出一个最小值。
大顶堆与此类似不在此。大顶堆每次操作完毕就会从未排序的数组中选出一个最大值。
二、此算法的C++实现过程
// HeadSort.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
void HeapSort(int num[], int size);
void BuildHeap(int num[], int size);
void PercolateDown(int num[], int index, int size);
void PrintHeap(int array[], int nLength);
void Swap(int num[], int v, int u);
int main(int argc, char *argv[])
{
int data[9] = { 8, 5, 4, 6, 23, 7, 1, 9, 12 };
HeapSort(data, 9);
system("PAUSE");
return 0;
}
void HeapSort(int num[], int size)
{
int i;
int iLength = size;
printf("the original array is:\n");
PrintHeap(num, iLength);
printf("*****Begin build the minor bunch!!*****\n");
BuildHeap(num, size);// 建立小顶堆
printf("*****Begin delete the minimum value!!*****\n");
for (i = iLength - 1; i >= 1; i--)
{
Swap(num, 0, i);// 交换
size--;// 每交换一次让规模减少一次
printf("Now has %d numbers sorted completely:\n", iLength - i);
PercolateDown(num, 0, size);// 将新的首元素下滤操作
PrintHeap(num, iLength);
}
}
// 建堆方法,只需线性时间建好
void BuildHeap(int num[], int size)
{
int i;
for (i = size / 2 - 1; i >= 0; i--)
{// 对前一半的节点(解释为“从最后一个非叶子节点开始,将每个父节点都调整为最小堆”更合理一些)
printf("Begin build the minor bunch at PercolatDown\n");
PercolateDown(num, i, size);// 进行下滤操作
printf("Begin build the minor bunch at %d \n", i);
PrintHeap(num, size);
}
}
// 对该数进行下滤操作,直到该数比左右节点都小就停止下滤
void PercolateDown(int num[], int index, int size)
{
int min;// 设置最小指向下标
while (index * 2 + 1 < size)
{// 如果该数有左节点,则假设左节点最小
min = index * 2 + 1;// 获取左节点的下标
if (index * 2 + 2 < size)
{// 如果该数还有右节点
if (num[min] > num[index * 2 + 2])
{// 就和左节点分出最小者
min = index * 2 + 2;// 此时右节点更小,则更新min的指向下标
}
}
// 此时进行该数和最小者进行比较,
if (num[index] < num[min])
{// 如果index最小,
break;// 停止下滤操作
}
else
{
Swap(num, index, min);// 交换两个数,让大数往下沉
index = min;// 更新index的指向
}
}// while
}
// 给定数组交换两个数的位置
void Swap(int num[], int v, int u)///u v is the index of elements
{
int temp = num[v];
num[v] = num[u];
num[u] = temp;
}
void PrintHeap(int array[], int nLength)
{
int i;
for (i = 0; i < nLength; i++)
{
printf("%d ", array[i]);
}
printf("\n");
}