堆排序
前一篇博客中我们列举了对小根堆的相关操作。
想把一个数组按照从小到大的顺序来排列,需要用到大根堆。同理,从大到小排序需要小根堆。
我们现在试着把一个数组从小到大排序,那么就需要用到大根堆。
每次把根与堆的第 len 个元素交换,然后对大小为 len - 1 的堆进行重建堆。不断重复上述过程,直到堆中只剩一个元素。
堆排序需要用到向下筛选算法,和重建堆的算法,时间复杂度为 O( N logN )。
//大根堆的向下筛选算法
void SiftDown(int* arr,int start,int m){
int i = start;
int j = 2*i + 1; //左孩子
int temp = arr[i];
while(j <= m){
if(j < m && arr[j] < arr[j+1]) j++; //选两孩子的大者
if(temp >= arr[j]) break; //根比大孩子大
else{ //大孩子上移
arr[i] = arr[j];
i = j; //大孩子的位置给根
j = 2*j + 1; //继续向下筛选
}
arr[i] = temp; //找到自己目前合适的位置
}
}
//堆排序
void HeapSort(int* arr,int len){
//建初堆
for(int i = len/2-1;i >= 0;i --) //从第一个非叶子结点开始比较
SiftDown(arr,i,len-1); //向下筛选
//每趟确定一个元素,需n-1趟
for(int i = len-1;i >= 1;i --){
//根与末尾进行交换
int temp = arr[i];
arr[i] = arr[0];
arr[0] = temp;
//重建大根堆
SiftDown(arr,0,i-1);
}
}
堆排序和简单选择排序同属于选择排序,是不稳定排序。
完整代码:
#include<iostream>
using namespace std;
//大根堆的向下筛选算法
void SiftDown(int* arr,int start,int m){
int i = start;
int j = 2*i + 1; //左孩子
int temp = arr[i];
while(j <= m){
if(j < m && arr[j] < arr[j+1]) j++; //选两孩子的大者
if(temp >= arr[j]) break; //根比大孩子大
else{ //大孩子上移
arr[i] = arr[j];
i = j; //大孩子的位置给根
j = 2*j + 1; //继续向下筛选
}
arr[i] = temp; //找到自己目前合适的位置
}
}
//堆排序
void HeapSort(int* arr,int len){
//建初堆
for(int i = len/2-1;i >= 0;i --) //从第一个非叶子结点开始比较
SiftDown(arr,i,len-1); //向下筛选
//每趟确定一个元素,需n-1趟
for(int i = len-1;i >= 1;i --){
//根与末尾进行交换
int temp = arr[i];
arr[i] = arr[0];
arr[0] = temp;
//重建大根堆
SiftDown(arr,0,i-1);
}
}
int main(){
int arr[] = {3,4,1,7,6,8,2,5,9,0};
for(int i = 0;i < 10;i ++) cout<<arr[i]<<" ";
cout<<endl;
HeapSort(arr,10);
for(int i = 0;i < 10;i ++) cout<<arr[i]<<" ";
}
运行结果: