堆排序可分为两部分,第一部分是建堆,第二部分是取出一个元素后调整堆。堆排序可以分成两种方式,构建最大堆(用于升序排列)和最小堆(用于降序排列),两者的差别只是修改代码中adjust函数中arr[index<arr[left]和arr[index]<arr[right]中的两个小于号改成大于号就可以。堆是用完全二叉树定义的,满足如下性质:有一个节点k,它的父节点是(k-1)/2,它的左子节点是2k+1,右子节点是2k+2。最大堆定义,每个节点都大于它的左右孩子。最小堆定义,每个节点都小于它的左右孩子。这个概念和搜索二叉树不一样。
首先构建堆,不是说真的做个堆的结构出来,而是说对堆化这个数组,然后每次把堆头元素与堆尾交换,相当于把堆尾的元素拿出去了,再将前len-1个元素重新调整,最后输出的结果就是排序结果了。
最大堆和最小堆用于升序或降序的区别,就在于每次被拿到数组尾巴的是大元素还是小元素。
//辅助交换函数
void Swap(int &a, int &b)
{
int temp = a;
a = b;
b = temp;
}
void adjust(int arr[], int root, int len)
{
int left = 2 * root + 1; // index的左子节点
int right = 2 * root + 2; // index的右子节点
int index = root; //左右中三者的最大值节点
if (left < len && arr[index] < arr[left])
{
index = left;
}
if (right < len && arr[index] < arr[right])
{
index = right;
}
if (index != root)
{
Swap(arr[index], arr[root]);
adjust(arr, index, len);
}
}
// 堆排序
void heapsort(int arr[], int len)
{
// 构建大根堆(从最后一个非叶子节点向上)
for (int i = len / 2 - 1; i >= 0; i--)
{
adjust(arr, i, len);
}
// 调整大根堆
for (int j = len - 1; j >= 1; j--)
{
Swap(arr[0], arr[j]); // 将当前最大的放置到数组末尾
adjust(arr, 0, j); // 将未完成排序的部分继续进行堆排序
}
}