堆排序
- 选择排序的一种
堆
- 要求(以大顶堆为例):
- 是一颗完全二叉树(叶子结点只能在最后两层,最底层的叶子结点自左到右要连续无空缺)
- 每个节点的值都大于其所有子节点
算法性能
- 时间复杂度:O(nlogn)
- 空间复杂度:O(1)
- 稳定性:不稳定
代码演示
代码1
- 首先为了方便理解算法思路,代码1 为堆单独开辟一块空间,空间复杂度变为O(n).
#include <iostream>
using namespace std;
int a[] = {9, 7, 2, 5, 4, 2, 7};
int len = sizeof(a)/sizeof(int);
int main(){
//堆(二叉树)的存储从下标1开始
int heap[len+1];
//无序数组构造成大顶堆
for(int i = 1; i <= len; ++i){
//按照完全二叉树的规则添加一个元素
heap[i] = a[i-1];
//当前调整节点下标
int t = i;
//按照大顶堆的规则调整二叉树
//父节点存在 && 节点值大于父节点
while(t/2 > 0 && heap[t] > heap[t/2]){
//交换父子节点位置
int tt = heap[t];
heap[t] = heap[t/2];
heap[t/2] = tt;
//当前节点切换为父节点
t = t/2;
}
}
//大顶堆拆成有序数组
for(int i = 1; i <= len; ++i){
//堆的根节点放在结果集的尾部
a[len-i] = heap[1];
//完全二叉树最后一层的最右叶节点成为新的根节点
heap[1] = heap[len + 1 -i];
//按大顶堆规则调整二叉树
//当前调整节点下标
int t = 1;
//左孩子存在
while(t*2 < len+1-i){
//当前节点的值若小于孩子节点
//右孩子存在 && 右孩子节点值大于当前节点
if(t*2 + 1 < len+1-i && heap[t*2 + 1] > heap[t]){
//右孩子节点值大于左孩子
if(heap[t*2 + 1] > heap[t*2]){
//和右孩子交换位置
int tt = heap[t];
heap[t] = heap[t*2 + 1];
heap[t*2 + 1] = tt;
t = t*2 + 1;
}else{
//和左孩子交换位置
int tt = heap[t];
heap[t] = heap[t*2];
heap[t*2] = tt;
t = t*2;
}
}else if(heap[t*2] > heap[t]){ //左孩子节点值大于当前节点
//和左孩子交换位置
int tt = heap[t];
heap[t] = heap[t*2];
heap[t*2] = tt;
t = t*2;
}else{
break;
}
}
}
//输出结果
for(int i = 0; i < len; ++i){
cout << a[i] << " ";
}
return 0;
}
代码2
- 正常写法,直接在原数组空间内建堆
- 空间复杂度 O(1)
#include <iostream>
using namespace std;
//从下标1开始存储
int a[] = {-1, 1, 8, 2, 5, 41, 2, 7};
int len = sizeof(a)/sizeof(int) - 1;
int main(){
//无序数组转化成大顶堆
for(int i = 1; i <= len; ++i){
//堆中新增节点下标
int t = i;
//按照大顶堆的规则调整二叉树
//父节点存在 && 节点值大于父节点
while(t/2 > 0 && a[t] > a[t/2]){
//交换父子节点位置
int tt = a[t];
a[t] = a[t/2];
a[t/2] = tt;
//当前节点切换为父节点
t = t/2;
}
}
//大顶堆转化为有序数组
for(int i = 1; i <= len; ++i){
//堆的根节点和最右叶节点交换
int tt = a[1];
a[1] = a[len+1-i];
a[len+1-i] = tt;
//按大顶堆规则调整二叉树(1 ~ len-i)
//当前调整节点下标
int t = 1;
//左孩子存在
while(t*2 < len+1-i){
//右孩子存在 && 右孩子节点值大于当前节点
if(t*2 + 1 < len+1-i && a[t*2 + 1] > a[t]){
//右孩子节点值大于左孩子
if(a[t*2 + 1] > a[t*2]){
//和右孩子交换位置
int tt = a[t];
a[t] = a[t*2 + 1];
a[t*2 + 1] = tt;
t = t*2 + 1;
}else{
//和左孩子交换位置
int tt = a[t];
a[t] = a[t*2];
a[t*2] = tt;
t = t*2;
}
}else if(a[t*2] > a[t]){ //左孩子节点值大于当前节点
//和左孩子交换位置
int tt = a[t];
a[t] = a[t*2];
a[t*2] = tt;
t = t*2;
}else{
break;
}
}
}
//输出结果
for(int i = 1; i <= len; ++i){
cout << a[i] << " ";
}
return 0;
}