1. 堆的基本概念
1. 堆分为大根堆、小根堆,逻辑上是一棵完全二叉树
2. 大根堆:对于树中任意一个结点n,n大于它的左孩子、右孩子,根结点最大
3. 小根堆:对于树中任意一个结点n,n小于它的左孩子、右孩子,根结点最小
4. 使用大根堆排序得到递增序列,使用小根堆排序得到递减序列
2. 堆排序的大致过程
1. 将待排序序列建立成大根堆
2. 将第一个也就是最大的元素与最后一个元素交换
3. 对于交换后的第一个元素,重新调整为大根堆
4. 重复2、3,一共n-1次
3. 关键所在
调整为大根堆是关键,下面是代码
//将以k为根的子树调整为大根堆
void headAdjust(int A[], int k, int len) {
A[0] = A[k]; //A[0]暂存子树根结点
for (int i = 2 * k; i <= len; i *= 2) { //2k是k的左孩子,沿key较大的子结点向下筛选
if (i < len && A[i] < A[i + 1]) { //i+1是k的右孩子
i++; //若右孩子大则令i=i+1
}
//此时A[i]为k的左右孩子中大的那个
if (A[0] >= A[i]) { //若k大于等于它左右孩子最大的那个
break; //直接退出,不用调整
}
else { //若k小于它左右孩子最大的那个
A[k] = A[i]; //将k最大的孩子赋给k
k = i; //此时相当于k下坠到i的位置
}
}
A[k] = A[0]; //调整完毕
}
4. 完整代码案例
#include<iostream>
using namespace std;
//交换a,b的值
void swap(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
//将以k为根的子树调整为大根堆
void headAdjust(int A[], int k, int len) {
A[0] = A[k]; //A[0]暂存子树根结点
for (int i = 2 * k; i <= len; i *= 2) { //2k是k的左孩子,沿key较大的子结点向下筛选
if (i < len && A[i] < A[i + 1]) { //i+1是k的右孩子
i++; //若右孩子大则令i=i+1
}
//此时A[i]为k的左右孩子中大的那个
if (A[0] >= A[i]) { //若k大于等于它左右孩子最大的那个
break; //直接退出,不用调整
}
else { //若k小于它左右孩子最大的那个
A[k] = A[i]; //将k最大的孩子赋给k
k = i; //此时相当于k下坠到i的位置
}
}
A[k] = A[0]; //调整完毕
}
//建立大根堆
void buildMaxHeap(int A[], int len) {
for (int i = len / 2; i > 0; i--) {
headAdjust(A, i, len);
}
}
//堆排序
void heapSort(int A[], int len) {
buildMaxHeap(A, len); //建立大根堆
for (int i = len; i > 1; i--) { //一共n-1趟交换
swap(A[i], A[1]); //堆顶和堆底交换
headAdjust(A, 1, i - 1); //调整,把剩余的i-1个元素调整为大根堆
}
}
int main() {
int arr[] = { 0,1,4,5,2,6,3 };
heapSort(arr, 6);
for (int i = 1; i <= 6; i++) {
cout << arr[i] << " ";
}
return 0;
}
5. 运行结果
6. 时间复杂度
最好最坏平均都是O(nlogn)
7. 空间复杂度
O(1)
8. 稳定性
不稳定