一、概念
1.定义
(1)堆heap
堆的本质是个数组,但是可以看成一个近似的完全二叉树。
(2)最大堆max-heap
根结点的值大于等于子结点的值,根结点(堆顶)的值始终是最大的。
(3)最小堆min-heap
跟结点的值小于等于子结点的值
(4) height
根结点到叶结点最长简单路径上边的数目。
2.性质
(1)堆可以被视为一棵完全二叉树,二叉树的层次遍历结果与数组元素的顺序对应,树根为A[1]。对于数组中第i个元素,可得其父节点,子结点的下标
PARENT(i)
return i/2
LEFT(i)
return 2i
RIGHT(i)
return 2i+1
二、程序
1.堆的结构
A[N]:堆数组
length[A]:数组中元素的个数
heap-size[A]:存放在A中的堆的元素个数
2.在堆上的操作
(1)MAX-HEAPIFY(A, i),维护最大堆的性质,时间复杂度为O(lgn)。
(2)BUILD-MAX-HEAP(A),从无序数组中构造最大堆,时间复杂度为O(n)。
(3)HEAPSORT(A),堆排序,时间复杂度为O(nlgn)。
3.堆的应用
利用堆实现一个最大优先级队列,最大优先队列的实质就是无论进行什么操作,始终维护最大堆的性质,堆顶始终保持最大,优先级最高
(1)HEAP-MAXIMUM(A),返回堆中的最大元素,时间复杂度为O(1)。
(2)HEAP-INCREASE-KEY(A, i, key),用key代替下标为i的元素,O(lgn)。
(3)HEAP-EXTRACT-KEY(A),去掉堆顶并返回,还要维护堆的性质,O(lgn)。
(4)MAX-HEAP-INSERT(A, key),插入元素,并维护堆的性质,O(lgn)。
(5) HEAP-DELETE(A,i),删除下标为i的元素,并维护堆的性质,O(lgn)。
(5) HEAP-DELETE(A,i),删除下标为i的元素,并维护堆的性质,O(lgn)。
代码:
//头文件
#include <iostream>
#include <stdio.h>
using namespace std;
//宏定义
#define N 1000
#define PARENT(i) (i)>>1<span style="white-space:pre"> </span>
#define LEFT(i) (i)<<1
#define RIGHT(i) ((i)<<1)+1
class Heap
{
public:
//成员变量
int A[N+1];
int length;
int heap_size;
//构造与析构
Heap(){}
Heap(int size):length(size),heap_size(size){}
~Heap(){}
//功能函数
void Max_Heapify(int i);
void Build_Max_Heap();
void HeapSort();
//优先队列函数
void Heap_Increase_Key(int i, int key);
void Max_Heap_Insert(int key);
int Heap_Maximum();
int Heap_Extract_Max();
void Heap_Delete(int i);
//辅助函数
void Print();
};
//使以i结点为根结点的子树成为最大堆,调用条件是确定i的左右子树已经是堆,时间是O(lgn)
//递归方法
void Heap::Max_Heapify(int i)
{
Print();
int l = LEFT(i), r = RIGHT(i), largest;
//选择i、i的左、i的右三个结点中值最大的结点
if(l <= heap_size && A[l] > A[i])
largest = l;
else largest = i;
if(r <= heap_size && A[r] > A[largest])
largest = r;
//如果根最大,已经满足堆的条件,函数停止
//否则
if(largest != i)
{
//根与值最大的结点交互
swap(A[i], A[largest]);
//交换可能破坏子树的堆,重新调整子树
Max_Heapify(largest);
}
}
//建堆,时间是O(nlgn)
void Heap::Build_Max_Heap()
{
heap_size = length;
//从堆中中间元素开始,依次调整每个结点,使其符合堆的性质,因为堆的后半段数组都是树的叶子结点,每个叶结点可以看成只包含一个元素的堆,因此可以从length/2开始调用<span style="font-family: Arial;">Max_Heapify去建立一个堆</span>
for(int i = length / 2; i >= 1; i--)
Max_Heapify(i);
}
//堆排序,从小到大,每次把堆顶(最大)与最后一个元素交换,这个最大的值即确定,然后对前面的堆维护最大堆的性质,循环即可。时间是O(nlgn)
void Heap::HeapSort()
{
//使无序数组成为最大堆
Build_Max_Heap();
for(int i = length; i > 1; i--)
{
//将最大的元素即堆顶放到最后
swap(A[1], A[i]);
//堆的大小减一,然后再对前heap_size-1个元素维护堆的性质
heap_size--;
//堆顶由于刚才的swap破坏了性质,调用<span style="font-family: Arial;">Max_Heapify使其重新变成最大堆</span>
Max_Heapify(1);
}
}
//将元素i的关键字增加到key,要求key>=A[i]
void Heap::Heap_Increase_Key(int i, int key)
{
if(key < A[i])
{
cout<<"new key is smaller than current key"<<endl;
exit(0);
}
A[i] = key;
//跟父比较,若A[PARENT(i)]<A[i],则交换
//若运行到某个结点时A[PARENT(i)]>A[i],就跳出循环
while(A[PARENT(i)] > 0 && A[PARENT(i)] < A[i])
{
swap(A[PARENT(i)], A[i]);
i = PARENT(i);
}
}
//把key插入到集合A中
void Heap::Max_Heap_Insert(int key)
{
if(heap_size == N)
{
cout<<"heap is full"<<endl;
exit(0);
}
heap_size++;length++;
A[heap_size] = -0x7fffffff;
Heap_Increase_Key(heap_size, key);
}
//返回A中最大关键字,时间O(1)
int Heap::Heap_Maximum()
{
return A[1];
}
//去掉并返回A中最大关键字,时间O(lgn)
int Heap::Heap_Extract_Max()
{
if(heap_size < 1)
{
cout<<"heap underflow"<<endl;
exit(0);
}
//取出最大值
int max = A[1];
//将最后一个元素补到最大值的位置
A[1] = A[heap_size];
heap_size--;
//重新调整根结点
Max_Heapify(1);
//返回最大值
return max;
}
//删除堆中第i个元素
void Heap::Heap_Delete(int i)
{
if(i > heap_size)
{
cout<<"there's no node i"<<endl;
exit(0);
}
//把最后一个元素补到第i个元素的位置
int key = A[heap_size];
heap_size--;
//如果新值比原A[i]大,则向上调整
if(key > A[i])
Heap_Increase_Key(i, key);
else//否则,向下调整
{
A[i] = key;
Max_Heapify(i);
}
}
void Heap::Print()
{
int i;
for(i = 1; i <= length; i++)
{
if(i > 1)cout<<',';
else cout<<"==> A = {";
cout<<A[i];
}
cout<<'}'<<endl;
}