一、堆排序的定义:
利用大根堆或小根堆(都属于完全二叉树)这种数据结构所设计的一种排序算法,它是选择排序的一种。
- 时间复杂度:平均:O(nlogn) 最快:O(nlogn) 最慢:O(nlogn)
- 空间复杂度:O(1)
二、完全二叉树的特点:
1、 一个完全二叉树的形式一定是这样:
2、如果第一个根结点的深度为1,编号为0号
- 那么一个深度为i的满二叉树,它的节点数为2^i-1;
- 那么每一层的节点数为2^(i-1);
- 一个共有n个结点的完全二叉树,最后一个非叶子结点的编号为n/2-1(向下取整);
- 它的第n号结点的左孩子结点编号为2n+1,右孩子结点为2n+2;
三、堆排序的流程
1、构造最大堆(或最小堆)
如果要求从小到大排序,就最大堆,如果要求从大到小排序,就最小堆。
1.1从下往上堆
void BuildMaxHeap(int *A, int n){
int i;
for (i = n / 2 - 1; i >= 0; i--){//从最后一个非叶子结点开始,向左向上遍历
if (A[i] < A[2 * i + 1]){//如果左孩子结点比它大,就与左孩子结点交换
int temp = A[2 * i + 1];
A[2 * i + 1] = A[i];
A[i] = temp;
}
if (2 * i + 2<n&&A[i] < A[2 * i + 2]){//如果有右孩子结点,且比它大,就与右孩子结点交换
int temp = A[2 * i + 2];
A[2 * i + 2] = A[i];
A[i] = temp;
}
}
}
1.2从上往下调
交换到上面的结点后,它的孩子如果是非叶子结点,就需要重新调整
void AdjustHeap(int *A, int n){
int i;
for (i = 0; i <= n / 2 - 1; i++){
if (2 * i + 2 < n){//右孩子存在,比较三者,与最大的孩子交换
if (A[2 * i + 1] < A[2 * i + 2] && A[i]<A[2 * i + 2]){//右孩子最大
int temp = A[2 * i +2];
A[2 * i +2] = A[i];
A[i] = temp;
}
else if (A[2 * i + 1] >= A[2 * i + 2] && A[i]<A[2 * i + 1]){//左孩子最大
int temp = A[2 * i + 1];
A[2 * i + 1] = A[i];
A[i] = temp;
}
}
else if (A[i] < A[2 * i + 1]){//左孩子比它大
int temp = A[2 * i + 1];
A[2 * i + 1] = A[i];
A[i] = temp;
}
}
}
2、将最后的结点与堆顶对调,重新调整最大堆(步骤1.2)
3、将最后的结点排除在堆外,重新进行步骤2
class HeapSort {
public:
int* heapSort(int* A, int n) {
if (n == 0 || n == 1)//空数组或只有1个元素,不需要排序
return A;
BuildMaxHeap(A, n);//构造最大堆
int len = n;
while (len>1){
AdjustHeap(A, len);//从上往下调整
int temp = A[0];//最后的结点与堆顶对调
A[0] = A[len - 1];
A[len - 1] = temp;
len--;//将最后一个结点排除在堆外
}
return A;
}
};