数据结构之堆(优先队列)
二叉堆
概念:
-
优先队列是一个根据优先性而先去执行操作的一种特殊队列,平常队列是先进先出,但是优先队列是根据优先级选择先出的元素。优先队列的主要操作有插入和删除最小值
-
堆(heap)通常是指二叉堆,因为二叉堆的使用次数最频繁,所以我们平常认为的堆就是二叉堆
-
二叉堆:二叉堆是由一颗完全二叉树实现的,完全二叉树是**一颗底层必须从左往右依次插入且底层以上的节点都是填满的树(最后一层也就是叶子节点可以不是填满的,但是已经插入的元素必须是从左到右的中间不空缺元素的)**称为完全二叉树
-
完全二叉树是一个很有规律的树,因此用一个动态数组实现是效率最好的选择,在动态数组中是按照从上到下从左到右依次插入数组中,数组下标为0处不放元素。
用数组实现的完全二叉树的性质有:
- 对于数组中任意一个位置i上的元素,其左儿子在数组位置2i上,右儿子在左儿子后的单元也就是数组下标为(2i+1)的位置,它的父亲在i/2的位置上。
- 数组下标为0的地方是不放我们要存的元素的
性质:
- 堆也就是通常认为的二叉堆(binary heap),是由一颗完全二叉树实现的
- 堆中的主要操作有插入和删除最小值操作,堆的删除其实就是删除最小值而不是删除指定值
- 堆有堆序性质,也就是堆中的任意子树也是一个堆,因此任意节点就应该小于它的子节点,因为堆需要快速查找最小元,因此我们将最小元放在根节点上。
- 其结构体应该包含一个数组(动态),堆中存储的有效元素个数,数组的容量以便于扩容
最大堆/大根堆:任意节点应该大于它的所有后裔
最小堆/小根堆:任意节点应该小于它的所有后裔
插入操作(上浮):
- 先遵从堆从上到下,从左到右依次插入,我们将数据按照这个准则插入到数组。
- 然而,插入后,我们还需要进行检查这个堆是否有堆序性,如果没有就需要进行调整
- 插入数据跟根节点进行比较,如果根节点的值比插入数据大,就需要交换,然后一路向上检查比较,直到根节点。
删除操作(下沉):
- 堆的删除操作是删除最小值的操作,而不是删除指定值的操作
- 我们创建堆时,都将最小值放到根节点,因此删除我们只需要将根节点删除掉
- 但是删除根节点后,我们的堆序性就无法保证,如何保证堆序性以及堆的性质(也就是完全二叉树的性质)呢
- 我们需要将根节点的值与叶子层的最右边元素进行交换,然后删除交换后处在叶子节点(也就是原来的根节点)的节点,然后让现在的根节点跟左右子节点的最小节点比较,如果大于就交换,直到检查到叶子节点,则就调整成功了。
构建堆
- 构建堆的操作就是将一个无序的树,构造成一个堆
- 一个堆满足完全二叉树的特性以及堆序性,因此我们需要对这个无序树进行调整,让其成为一个堆。
实现代码:
class heap{
public:heap(int maxsize){key=new int[maxsize+1];key[0]=0;for(int i=1;i<maxsize+1;i++){key[i]=0;}size=0;capacity=maxsize;}int find(int data){for(int i=1;i<size;i++){if(key[i]==data){return i;}}cout<<"没有找到"<<endl;return -1;}void adjust(int data){key[size+1]=data;int index=size+1;while(index>0){int i=index;index=index/2;if(key[index]>key[i]){int temp=key[index];key[index]=key[i];key[i]=temp;}}}void insert(int data){if(size==capacity){int *an=new int[2*capacity];capacity=capacity*2;for(int i=0;i<=size;i++){an[i]=key[i];}delete[] key;key=an;}if(key[1]==0){key[1]=data;}else{adjust(data);}size++;}void deladjust(int index){if(index*2<=size){int mn;if(key[index*2+1]==NULL){mn=index*2;}else if(key[index*2]>key[index*2+1]){mn=index*2+1;}else{mn=index*2;}if(key[index]>key[mn]){int temp=key[index];key[index]=key[mn];key[mn]=temp;}deladjust(mn);}return;}void delmin(){key[1]=key[size];key[size]=0;size--;deladjust(1);}void destroy(){for(int i=1;i<=size;i++){key[i]=0;}size=0;cout<<"成功清除堆中数据"<<endl;}void print(){int cnt=1,ans=1;for(int i=1;i<=size;i++){cout<<key[i]<<" ";if(i==ans){cout<<endl;ans=pow(2,cnt)+i;cnt++;}}}
private:int *key; //动态数组,用来存储树节点的元素int size; //堆中存在的有效元素个数int capacity; //数组容量
};
尾言
完整版笔记也就是数据结构与算法专栏完整版可到我的博客进行查看,或者在github库中自取(包含源代码)
- 博客1: codebooks.xyz
- 博客2:moonfordream.github.io
- github项目地址:Data-Structure-and-Algorithms