快速排序和归并排序都采用了分而治之、递归的思想。一直没仔细研究下堆排序,每次都忘,感觉好像蛮复杂的,在网上搜了下别人的文章,感觉写得好难的样子,顿时没读下去的动力了。个人喜欢简短准确的,于是翻开当年的《数据结构》,才三页,还不少图,正和我意,于是实现之。
堆排序的思想是不断地取堆顶元素,取完就结束。但要解决两个问题,一是建堆,二是调整堆。建堆其实也是通过由下往上调整堆实现的。简单吧?
简单的讲就是,把一维数组看成完全二叉树,构建成堆,于是二叉树中所有非叶子结点的值都不大于(或不小于)其左右孩子结点的值。这样堆顶肯定是数组中的最大值或最小值了,即对应所谓的大顶堆和小顶堆。
实现细节见代码,有细致的注释,不过比较拗口、啰嗦,不知道别人读不读得懂。。
#include<iostream>
using namespace std;
//交换a、b
void swap(int &a, int &b)
{
int temp = a;
a = b;
b = temp;
}
void HeapAdjust(int *arr, int low, int high)//堆调整 使其成为大顶堆
{
int temp = arr[low];//要调整的堆顶元素
for(int j = 2*low+1; j <= high; j=j*2+1) //找到堆顶的左结点
{
if(j < high && arr[j] < arr[j+1])//存在右节点&&右节点大
j++;
if(temp > arr[j]) //原先的堆顶元素最大
break;//直接跳出循环 否则往下 后面写else也没问题
arr[low] = arr[j];//将大的元素给原先堆顶元素现在的位置(可能是一开始的堆顶也可能是中间层的堆顶)
low = j;//保存大的元素位置
}
arr[low] = temp;//循环结束后用原先的堆顶元素填补大的元素位置
}
void HeapSort(int *arr, int length)
{
for(int i=length/2-1; i >= 0; --i)
HeapAdjust(arr , i, length-1); //从完全二叉树的最后一个非叶子节点开始往上逐渐调整堆,即建堆的过程
for(i=length-1; i > 0; --i)
{
swap(arr[0], arr[i]);//交换堆顶元素(大的数)和arr[0..i]中最后一个元素 这样排序出来是非递减的(数组中没有相同元素的话就是从小到大排序)
HeapAdjust(arr, 0, i-1); //调整剩下的堆
}
}
void Print(int *arr, int length)//打印
{
for(int i = 0; i < length; i++)
{
printf("%6d",arr[i]);
if(0 == (i+1)%10)
cout<<endl;
}
cout<<endl;
}
void main()
{
int arr[8] = {49,38,65,97,76,13,27,49};
int length = 8;
HeapSort(arr, length);
Print(arr,length);
}