堆是一种完全二叉树,第i个节点的左孩子是2 * i + 1,右孩子是 2 * i + 2,可以通过子节点求父节点:(i - 1)/ 2 。不管是左孩子还是右孩子都可以通过这种方式求的其父节点。对于一个数组,可以先将数组中的元素构建为一个大根堆,这样数组中的第一个数就是最大的值,将这个数与最后一个位置上的数进行交换,最后一个数就是最大值了,之后考虑对去掉这个元素的数组重新调整为一个大根堆,再将这个数与最后一个位置上的数进行排序,循环往复,直到只剩下一个元素。
比如要排序的数组为:5,7,0,6,8。
可以这样依次构建大根堆,将5和7放在如下图所示的二叉树位置:
在放入第二个元素7的时候,将其与父节点的数5进行比较,7>5,交换位置,得到下图:
放入第三个元素0,将其与父节点进行比较,将其作为7的右节点。
下一个元素是6,6的父节点是5,比较后交换,6到达新位置后再次和它的父节点7比较,比较之后不交换。
最后插入节点8,插入过程与上述过程类似。
至此,将每个元素插入构造大根堆的过程完成,开始第二阶段,将最大元素取出,剩下元素构造大根堆,取出最大元素的循环,直至大根堆中只剩下最后一个元素。
首先将最大元素8与大根堆的最后一个元素6进行交换,然后将8取出,8是数组中最大的元素,6插入新的位置。
将6的两个孩子节点与它进行比较,孩子节点中较大的那个与6进行交换,得到下面的结果:
接着,将7与去除8之后新的大根堆最后一个元素5进行交换,得到下图所示的结果:
5的左节点6比它大,进行交换:
下一步,将6与0交换。
最后,将0和5的值进行比较并交换。
至此,堆排序完成。堆排序的时间复杂度为O(NlgN)。
堆排序的过程可以分为两个阶段:第一阶段是将数组中的每一元素插入形成大根堆的过程,这个过程的时间复杂为O(lgN)。因为假如完成二叉树的节点数为N,则其高度为lgN级别。则i位置上的数插入,要先遍历之前i-1位置的上的数,时间复杂度为O(lg(i-1)),最后加起来的值收敛在lgN级别。第二阶段,每排好一个数,都得过一个高度,相当于进行一次插入的过程,所以堆排序的时间复杂度为O(NlogN)。但是堆排序一般在工程上不常用,因为它是不稳定排序,且常数项比较大。
按照上述思路进行堆排序的C++程序如下:
#include <iostream>
using namespace std;
void heapInsert(int arr[], int index) ;
void heapify(int arr[], int index, int size);
void swap(int arr[], int a, int b)
{
if(a == b) {
return ;
}
int c = arr[a];
arr[a] = arr[b];
arr[b] = c;
}
void heapSort(int arr[],int length) {
if(arr == NULL || length < 2 ) {
return ;
}
for(int i = 0; i < length; i++) {
heapInsert(arr, i);
}
int heapSize = length;
swap(arr, 0, --heapSize);
while(heapSize > 0) {
heapify(arr, 0, heapSize);
swap(arr, 0 , --heapSize);
}
}
void heapInsert(int arr[], int index) {
while(arr[index] > arr[(index -1)/2]) {
swap(arr, index, (index -1)/2);
index = (index -1)/2;
}
}
void heapify(int arr[], int index, int size) {
int left = index *2 +1;
while(left < size) {
int largest = left + 1 < size && arr[left +1] > arr[left] ? left + 1 : left;//右孩子不越界,且右孩子大于左孩子的情况下,largest是右孩子的下标
largest = arr[largest] > arr[index] ? largest : index;//左右两个孩子较大的那个值如果比当前值大,大的那个孩子的下标变为了largest,否则,当前值的下标为largest。
if(largest == index) {
break;
}
//某个孩子的值比当前值大,这个孩子的下标为largest
swap(arr,largest,index);
index = largest;
left = index * 2 + 1;
}
}
int main()
{
int arr[] = {0, 3, 4, 4, 6, 5, 4};
int len = sizeof(arr)/sizeof(arr[0]);
heapSort(arr, len);
for(int i = 0; i < len; i++) {
cout<<arr[i]<<endl;
}
return 0;
}