什么是堆
两个特性
结构性:用数组表示的完全二叉树
有序性:任一结点的关键字是其子树所有结点的最大值(或最小值)
“最大堆(MaxHeap)”,也称"大顶堆":最大值
最小堆(MinHeap)”,也称"小顶堆":最小值
构建最大堆:
建立堆的主要函数
存储结构体:
struct node{
int *data;
int size;
};
1 create() 初始化堆
typedef node *tree;
tree create(int size){
tree t=new node;
t->size=0;
t->data=(int *)malloc(sizeof(int)*(size+10));
t->data[0]=-1000000;//设置哨兵值!!!一定要设,不然会有意外
return t;
}
这里为什么要设置哨兵值呢?在下面分析
2 insert() 插入值
主要思路:往数组的最后一个位置+1插入值,然后和前面的父节点(因为是完全二叉树又是数组存储,其父节点可以直接用i/2表示)比较,如果比父节点大就和父节点替换位置
bool insert(tree t,int item){
int i=++t->size;//获取最后一个位置+1;
for(;t->data[i/2]<item;i/=2){//和前面的父节点比较,
//如果在上面没有设置哨兵值,会导致如果插入的节点比第一个根节点还大,那么该循环不会结束,会因为一直和data[0]比较而无限的循环下去
t->data[i]=t->data[i/2];
}
t->data[i]=item;//最后在该位置插入值
return true;
}
3delete() 删除根节点
思路:要删的肯定是根节点(最大堆就是删除一个最大值),接着取出数组中的最后一个元素,放在第一位,然后从第一位开始往后比较,比后面小,就和后面的子节点替换位置,同理,子节点用i2或者i2+1表示,所以判断的时候需要先比较子节点哪个比较大
这里要注意的是,if(child!=h->size)代表当前的节点是否还存在左右子节点,如果child==h->size,说明已经到了边界,那就没有左右子节点了
int Delete(tree h){
if(h->size==0){
cout<<"已经没有元素了";
return -1;
}
int parent,child;
int maxData=h->data[1];
int top=h->data[h->size--];//去除最后一个元素,放在根节点,
//下面这个循环用来寻找放置top的位置
for(parent=1;parent*2<=h->size;parent=child){
child=parent*2;
if(child!=h->size){//如果存在左右儿子的情况
if(h->data[child]<h->data[child+1])child++;//找出左右儿子的较大者
}
if(top>=h->data[child])break;//找到了
//否则让左右儿子较大的替换当前parent节点
else
h->data[parent]=h->data[child];
}
//最后放入
h->data[parent]=top;
return maxData;
}
全部代码以及测试:
#include<bits/stdc++.h>
using namespace std;
#define maxdata 100000
struct node{
int * data;
int size;
int capacity;
};
typedef node * tree;
tree create(int maxsize){
tree h=new node;
h->data=(int *)malloc(sizeof(int)*(maxsize+1));
h->size=0;
h->capacity=maxsize;
h->data[0]=maxdata;
return h;
}
bool insert(tree t,int item){
int i=++t->size;
for(;t->data[i/2]<item;i/=2){
t->data[i]=t->data[i/2];
}
t->data[i]=item;
return true;
}
void LevelOrderTraversal(tree H){
int i;
printf("层序遍历的结果是:");
for(i = 1;i<=H->size;i++){
printf("%d ",H->data[i]);
}
printf("\n");
}
int Delete(tree h){
if(h->size==0){
cout<<"已经没有元素了";
return -1;
}
int parent,child;
int maxData=h->data[1];
int top=h->data[h->size--];//去除最后一个元素,放在根节点,
//下面这个循环用来寻找放置top的位置
for(parent=1;parent*2<=h->size;parent=child){
child=parent*2;
if(child!=h->size){//如果存在左右儿子的情况
if(h->data[child]<h->data[child+1])child++;//找出左右儿子的较大者
}
if(top>=h->data[child])break;//找到了
//否则让左右儿子较大的替换当前parent节点
else
h->data[parent]=h->data[child];
}
//最后放入
h->data[parent]=top;
return maxData;
}
int main(){
tree h;
int maxsize=100;
h=create(maxsize);
insert(h,55);
insert(h,66);
insert(h,44);
insert(h,33);
insert(h,11);
insert(h,22);
insert(h,88);
insert(h,99);
LevelOrderTraversal(h);
cout<<"删除了一个最大的节点:"<<Delete(h)<<endl;
LevelOrderTraversal(h);
cout<<"删除了一个最大的节点:"<<Delete(h)<<endl;
LevelOrderTraversal(h);
cout<<"删除了一个最大的节点:"<<Delete(h)<<endl;
LevelOrderTraversal(h);
cout<<"删除了一个最大的节点:"<<Delete(h)<<endl;
LevelOrderTraversal(h);
}
**
最小堆的建立
**
最小堆的建立就是最大堆的反向操作,只需要改 个地方:
for(parent=1;parent*2<=h->size;parent=child){
child=parent*2;
if(child!=h->size){//如果存在左右儿子的情况
if(h->data[child]>h->data[child+1])child++;//找出左右儿子的较大者
}
if(top<=h->data[child])break;//找到了
//否则让左右儿子较大的替换当前parent节点
else
h->data[parent]=h->data[child];
}
bool insert(tree t,int item){
int i=++t->size;
for(;t->data[i/2]>item;i/=2){
t->data[i]=t->data[i/2];
}
t->data[i]=item;
return true;
}
构建堆的调整:
将 N 个元素直接按顺序存入,再调整各结点的位置(简单说来,对于从最后一个有孩子结点的结点来说,其本身结点和孩子结点共同构成"子最小堆",借助前面删除的想法,对每个"子最小堆"排序,当排序完成,整个最小堆也建立成功),时间代价是 O(n)O(n)O(n)
代码:
#include<bits/stdc++.h>
using namespace std;
#define maxdata 100000
struct node{
int * data;
int size;
int capacity;
};
/*
8
55 66 44 33
11 22 88 99
*/
typedef node * tree;
tree create(int maxsize){
tree h=new node;
h->data=(int *)malloc(sizeof(int)*(maxsize+1));
h->size=0;
h->capacity=maxsize;
h->data[0]=maxdata;
return h;
}
void LevelOrderTraversal(tree H){
int i;
printf("层序遍历的结果是:");
for(i = 1;i<=H->size;i++){
printf("%d ",H->data[i]);
}
printf("\n");
}
void sort1(tree h,int i){
int parent,child;
int data=h->data[i];
for(parent=i;parent*2<=h->size;parent=child){
child=parent*2;
if(child!=h->size){
if(h->data[child]<h->data[child+1])child++;
}
if(h->data[child]<=data)break;
else h->data[parent]=h->data[child];
}
h->data[parent]=data;
}
void adjust(tree h){
int i=h->size/2;
for(;i>0;i--){
sort1(h,i);//从最拥有子树的最小树开始--完全二叉树的特性,可以自己画个图验证一下
}
}
int main(){
tree h;
h=create(100);
int n;
cin>>n;
for(int i=0;i<n;i++){
cin>>h->data[++h->size];
}
adjust(h);
LevelOrderTraversal(h);
}