堆排序的时间复杂度是O(nlgn),堆排序具有空间原址性:任何时候都只需要常数个额外的元素空间存储临时数据。
1.堆
二叉堆是一个数组,可视为一个近似的完全二叉树,除最底层外,该树是完全充满的,而且是从左到右填充。堆的数组A包括两个属性,A.length给出数组元素的个数,A.heap-size表示有多少个堆元素存储在数组中。虽然A[1...A.length]可能都存储数据,但只有A[1...A.heap-size]中存放的是堆的有效元素。给定节点的下标i(从0开始编号),
父节点PARENT(i)=(i-1)/2
左节点LEFT(i)=2i+1
右节点RIGHT(i)=2i+2
最大堆:除根节点外所有节点i满足 A[PARENT(i)]>=A[i]。最小堆:除根节点外所有节点i满足 A[PARENT(i)]<=A[i]。堆排序算法中使用的是最大堆,最小堆通常用于构造有限队列。
2.函数及运行效率说明
2.1 fMaxheapify(int i);//建立i为根节点的最大堆,时间复杂度O(lgn),维护堆的性质。在调用fMaxheapify()时假定左右子树都是最大堆,是下标为i的根节点的子树重新遵循最大堆的性质。
2.2 fBuildheap(int n);//建立数组长度为n的最大堆,时间复杂度O(n)
fBuildheap(A)
A.heap-size = A.lengh
for i = (A.heap-size-1)/2 downto 0
fMaxheapify(i)
2.2 fHeapsort();//堆排序O(nlgn)
将数组构建成最大堆后,最大元素总是在根节点A[0],通过把A[0]与A[A.length]交换,从堆中去掉节点n(A.heap-size--),调用fMaxheapify(0),可以在A[0, A.length-1]上构建一个新的最大堆,重复这一过程。
HEAPSORT(A)
fBuildheap(A)
for i = A.length downto 1
exchange A[0] with A[i]
A.heap-size--
fMaxheapify(0)
3.源代码
#include <iostream>
using namespace std;
template <class T>
void fExchange(T &a, T &b)
{
T temp = b;
b = a;
a = temp;
}
template<class T>
class heapify
{
private:
T* heap;
int nHeapSize;//heap数组实际大小,下标从0计
int nMaxSize;//heap最大包含元素的个数,下标从1计
public:
heapify(T* h, int HS, int MS)
{
nHeapSize = HS-1;
nMaxSize = MS;
heap = h;
}
void fBuildheap(int n);//数组长度为n的最大堆
void fMaxheapify(int i);//建立i为根节点的最大堆
void fHeapsort();//堆排序O(nlgn)
void fInsert(T p);//插入新结点,结点值为p
T fExtractMAX();//删除根节点
void fIncreaseKey(int pos, T value);//将heap[i]的结点值增加为value
};
template<class T>
void heapify<T>::fMaxheapify(int i)
{
while (i < (nHeapSize+1)/2)//以循环结构代替递归,到叶子节点结束
{
int left = 2 * i + 1;//从0开始,所以左节点2i+1
int right = 2 * i + 2;
int max;
if (left <= nHeapSize && heap[left] > heap[i])
max = left;
else
max = i;
if (right <= nHeapSize && heap[right] > heap[max])
max = right;
if (max != i)
{
fExchange(heap[i], heap[max]);
i = max;
}
else//不交换,结束循环
break;
}
}
template<class T>
void heapify<T>::fBuildheap(int n)
{
for (int i = n / 2 - 1; i >= 0; i--)
{
fMaxheapify(i);
}
}
template<class T>
void heapify<T>::fInsert(T p)
{
nHeapSize++;
heap[nHeapSize] = p;
fIncreaseKey(nHeapSize, p);
}
template<class T>
void heapify<T>::fIncreaseKey(int pos, T value)
{
if (heap[pos] < value)
cout << "new key is no larger than current key\n";
else
{
heap[pos] = value;
while (pos >= 0 && heap[(pos - 1) / 2] < heap[pos])
{
fExchange(heap[(pos - 1) / 2], heap[(pos)]);
pos = (pos - 1) / 2;
}
}
}
template<class T>
T heapify<T>::fExtractMAX()
{
if (nHeapSize < 0)
cerr << "heap underflow\n";
else
{
T max = heap[0];
heap[0] = heap[nHeapSize];
nHeapSize--;
fMaxheapify(0);
return max;
}
}
template<class T>
void heapify<T>::fHeapsort()
{
int nNum = nHeapSize;
for (int i = nHeapSize; i >= 1; i--)//当nHeapSize=0时,堆“underflow”
{
fExchange(heap[nHeapSize], heap[0]);
nHeapSize--;
fMaxheapify(0);
}
for (int i = 0; i <=nNum; i++)
cout << heap[i] << " ";
cout << endl;
}
void main()
{
int A[15] = { 4, 1, 3, 2, 16, 9, 10, 14, 8, 7 };
heapify<int> Myheap(A, 10, 15);//构造函数,第一个变量指向数组指针,第二个变量表示设定heap实际包含的个数,第三个变量表示数组最大包含个数,两个变量的下标均从1计
Myheap.fBuildheap(10);
Myheap.fInsert(24);//插入新的元素后,因为Myheap的指针指向数组A,A的大小设定为10,所以插入后会导致数组越界。
int max = Myheap.fExtractMAX();//Run-Time Check Failure #2 - Stack around the variable 'A' was corrupted.所以A的大小可以申请大一些
Myheap.fHeapsort();
}