预备知识:
堆通常被看做一个近似完全的二叉树,使用数组A[1...N]表示堆,数组中一个元素代表堆上一个结点,堆存在以下性质:
根节点:A[1]
父结点:Parent[i] = i/2
左子节点:Left[i] = 2i
右子节点:Right[i] = 2i + 1
数组长度为:A.length
数组中有效数据长度为:A.heap-size
堆分为最大堆和最小堆,在最大堆中 Parent[i] >= A[i],在最小堆中 Parent[i] <= A[i]
维护堆的性质(MAX-HEAPIFY):调节父结点与子结点位置,使他们满足最大堆的大小关系。将A[i]的值在最大堆中“逐级下降”,从而使以下标i为根结点的子树重新遵循最大堆的性质。
MAX-HEAPIFY(A,i)
l = Left[i]
r = Right[i]
if l <= A.heap-size and A[l] > A[i]
lagest = l
else
lagest = i
if r <= A.heap-size and A[r] > A[lagest]
lagest = r
if i != lagest //当根结点不是最大值时
exchage A[i] with A[lagest]
MAX-HEAPIFY(A,lagest)//交换位置后,以该结点为根的子树可能违法最大堆的性质,所以需要递归调用
时间复杂度为:T(n) <= T(2/3n)+O(1) 根据主定理公式可以计算出 时间复杂度为O(lgn)
BUILD-MAX-HEAP(A)
A.heap-size = A.length
for i = [A.heap-size/2] downto 1 //此处i的值为A.heap-size/2向下取整的值,因为A[A.heap/2+1...A.heap-size]为叶子结点,没有子结点,所以只需考虑前半部分的值
MAX-HEAPIFY(A,i)
时间复杂度为 O(n)
堆排序算法(HEAPSORT):因为建堆后,数组中的数据并不是有序的,但最大值一定位于根结点A[1],根据此性质,可以进行排序
HEAPSORT(A)
BUILD-MAX-HEAP(A)
for i = A.length downto 2 //不断将最大值放在堆中最后一个元素位置,对应就是数组中由后向前进行排序
exchange A[1] with A[i] //将最大元素置后
A.heap-size = A.heap-size - 1 //有效长度减1,表示该元素已经归位
MAX-HEAPIFY(A,1) //因为将原堆末尾元素提到了根结点,所以需要对根结点重新执行MAX-HEAPIFY维护堆的性质。
时间复杂度为O(nlogn)
Demo:
#include <iostream>
#include <stdio.h>
using namespace std;
#define LENGTH 10
int heap_size = 0;
void swap(int *a,int *b)
{
int tmp;
tmp = *a;
*a = *b;
*b = tmp;
}
void max_headpify(int *A,int i)//维护堆的性质,下沉元素
{
int lagest;
int l = i*2;
int r = i*2+1;
if((l <= heap_size) && (A[l] > A[i]))
lagest = l;
else
lagest = i;
if((r <= heap_size) && (A[r] > A[lagest]))
lagest = r;
if(lagest != i)
{
swap(&A[i],&A[lagest]);
max_headpify(A,lagest);
}
}
void build_max_heap(int *A)//构建堆
{
heap_size = LENGTH;
for(int i = LENGTH/2;i >= 1;i--)
max_headpify(A,i);
}
void heapsort(int *A)//堆排序
{
build_max_heap(A);
for(int i = LENGTH;i >= 2;i--)
{
swap(&A[1],&A[i]);
heap_size--;
max_headpify(A,1);
}
}
int main()
{
int A[LENGTH+1];
for(int i = 1;i <= LENGTH;i++)
{
cin>>A[i];
}
heapsort(A);
for(int i = 1;i <= LENGTH;i++)
{
printf("%d\n",A[i]);
}
system("pause");
return 0;
}