本文主要介绍最大堆的常用算法。
最大堆定义:
最大堆中的每一个父节点的值均大于其子结点的值
而兄弟结点直接没有什么大小关系
最大堆的逻辑表示:
最大堆的逻辑结构是一棵完全二叉树
最大堆的物理存储结构:
由于最大堆的逻辑结构是一棵完全二叉树,因此可以通过数组来存储,(为了方便,从1开始存,下标0 号位置不存),因此下标为i 的结点,它的左儿子在 2i,右儿子在 2i+1,它的父结点在 i/2(按照整数除整数计算,例如5/2=2)
最大堆的常用算法:
1.由给定的一个数组序列,创建最大堆
2.删除堆顶元素(也就是最大元素),之后仍保持最大堆的特性
3.插入元素,同样插入后仍保持最大堆的特性
4.最大堆的初始化
下面给出具体的实现代码,以及测试,用c++实现,代码右边有注释
#include <iostream>
#include <algorithm>
using namespace std;
typedef struct maxheap
{
int *data;
int size;
int capacity;
maxheap():data(nullptr),size(0),capacity(0){}
}Mheap;
void initHeap(Mheap* &h,int si)
{
h=new Mheap;
h->data=new int[si+1]; //si 个元素,申请 si+1 个空间,因为0 号位置不存放元素
h->size=0;
h->capacity=si; //0号元素不存,从1-size,刚好size个元素可存储
}
void buildHeap(Mheap* &h,int num)
{
cout<<"please input data:"<<endl;
for(int i=1; i<num+1;i++) //先把数据从1 开始存入数组中
{
cin>>h->data[i];
}
h->size=num; //改变元素个数,与容量
h->capacity=h->capacity-num;
for(int i=h->size/2;i>0;i--) //从最后一个具有儿子结点的位置开始判断是否大于其子结点,一直向前判断
{
for(int j=i; j*2<=h->size;)
{
if(j*2==h->size) //考虑该结点为最后一个具有子结点的结点的情况
{
if(h->data[j]>h->data[2*j])
break;
else
{
swap(h->data[j],h->data[2*j]);
break;
}
}
if(h->data[j]>h->data[2*j]&&h->data[j]>h->data[2*j+1]) //非最后一个具有子结点的情况
{
break;
}
else
{
if(h->data[j*2] > h->data[2*j+1])
{
swap(h->data[j],h->data[2*j]);
j=2*j;
}
else
{
swap(h->data[j],h->data[2*j+1]);
j=2*j+1;
}
}
}
}
}
int PopTop(Mheap* &h) //删除堆中最大元素,并返回其值,删除后仍保持最大堆的特性
{
int max=h->data[1];
h->data[1]=h->data[h->size]; //把最后一个元素放在堆顶,然后进行最大堆操作
h->size--; //记得改变堆中元素个数 与容量
h->capacity++;
for(int i=1;2*i<=h->size;i++) //从堆顶部开始判断
{
if(i*2==h->size) //考虑其左儿子刚好为最后一个元素,即其无右儿子时
{
if(h->data[i]>h->data[2*i]) //只需比较自身与其 左儿子即可
break;
else
{
swap(h->data[i],h->data[2*i]); //因为这是最后一个有儿子的结点了,所以交换完父亲与儿子的值,直接退出循环
break;
}
}
if(h->data[i]>h->data[2*i]&&h->data[i]>h->data[2*i+1]) //这些情况就是,该结点未达到 最后一个具有儿子结点的情况
{ //只需 比较它与它的左右儿子
break;
}
else
{
if(h->data[i*2] > h->data[2*i+1])
{
swap(h->data[i],h->data[2*i]);
i=2*i; //下次判断他的儿子是否满足大于其子结点
}
else
{
swap(h->data[i],h->data[2*i+1]);
i=2*i+1;
}
}
}
return max;
}
void insertHeap(Mheap* &h,int key) //插入一个元素,之后保证满足最大堆的特性
{
h->size++; //先把最大堆的元素个数加一
h->data[h->size]=key; //之后把 要插入的元素放在 最后一个位置上
h->capacity--; //容量减一
for(int i=h->size;i>0;)
{ //从最后一个元素,开始判断是否满足其父母的值大于他的值
if(h->data[i/2]>h->data[i])
break; //满足的话直接跳出循环
else
{
swap(h->data[i/2],h->data[i]);
i=i/2; //继续判断他们的父母
}
}
}
ostream& operator <<(ostream& os,const Mheap* rhs) //重载流输出,方便测试
{
os<<" ";
for(int i=1;i<rhs->size+1;i++)
{
os<<rhs->data[i]<<" ";
}
os<<endl;
return os;
}
int main()
{
Mheap *h;
int si;
cout<<"please input the size of array: ";
cin>>si;
initHeap(h,si);
cout<<"please input the number of elements: ";
int num; // 目前要填入堆中的元素数量
cin>>num;
buildHeap(h,num);
cout<<h;
int innum;
cout<<"please input inserted number:";
cin>>innum;
insertHeap(h,innum);
cout<<h;
cout<<"max element is "<<PopTop(h)<<endl;
cout<<h;
}