应用--堆的建立,调整

什么是堆

在这里插入图片描述

两个特性

结构性:用数组表示的完全二叉树

有序性:任一结点的关键字是其子树所有结点的最大值(或最小值)

	“最大堆(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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值