堆是一个神奇的数据结构,是一个完全二叉树,满足:每个节点的值都不小于其左右孩子节点的值(我们称这种堆为大顶堆,因为根节点的值是最大的) 或者 小顶堆(把大顶堆定义反过来,根节点为最小值),我们可以用堆这种数据结构实现优先队列而且可以不用结构体来实现,一个普通的一维数组就可以了。
1.堆的实现
由于堆是一个完全二叉树,所以知道节点下表为i,那么它的左孩子就是2*i,右孩子就是2*i+1, 所以我们可以用数组来进行存储这个数据结构。 输入的时候当做是一个普通数组,然后在将这个普通数组转化成大顶堆。
转化成大顶堆:从下往上从右往左进行。 找到一个节点,将该节点与孩子节点进行判断:1.如果该节点值>孩子节点值,往下一个节点判断。 2.如果该节点值 < 孩子节点值,交换节点值且它们下标也进行交换(因为交换后不知道其左子树的最大值是什么),直到满足条件一。
纵观整个过程,我们是把节点往后调整,而且对于叶子节点来说没有孩子节点,所以调整过程就可以从n/2开始,其中n是总的节点数
2.节点插入
由于插入只能插入到最后一个位置,而插入的点有可能是最大的,我们就应该把这个点放到根节点的位置,对于这个节点来说是把它往上提,所以插入操作叫向上调整
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5;
int n;
int heap[maxn] = {0};
void downAdjust(int low,int high) ///向下调整 low表示现在位置,high表示总共的节点数
{
int i=low,j=i*2;
while(j <= high) //当存在左孩子并且在节点总数范围之内
{
if( heap[j+1] !=0 && heap[j]<heap[j+1]) ///如果存在右孩子并且右孩子的值>左孩子
{
j = j+1; ///将这个下标标记为右孩子
}
if(heap[j] > heap[i]) ///左右孩子中最大的值与根节点进行比较
{
swap(heap[i],heap[j]); ///不用自己写函数,c++标准库中有
i = j; ///注意不要忘记下标也要进行调整
j = i*2;
}else
{
break;
}
}
}
void CreatHeap()
{
for(int i=n/2; i>=0; i--)
{
downAdjust(i,n-1); ///对普通的数组进行调整
}
}
void upAdjust(int low,int high) ///插入时的向上调整
{
int j=high,i = j/2;
while(i >= low)
{
if(heap[j] > heap[i]) ///由于插入操作是在调整之后,所以不用考虑那么多,直接与根节点进行比较即可
{
swap(heap[i],heap[j]);
j = i;
i = j/2;
}else
{
break;
}
}
}
void Insert()
{
int num;
cin>>num;
heap[n++] = n;
upAdjust(0,n-1);
}
int main()
{
cin>>n;
for(int i=0; i<n; i++)
cin>>heap[i];
CreatHeap();
for(int i=0; i<n; i++)
cout<<heap[i]<<" ";
return 0;
}
3.堆排序
建堆完成后进行输出时发现只有跟节点是大的,而其它的点并不是按照从大到小或者从小到大进行输出。将根节点与最后一个点进行交换,然后除去最后一个点,进行向下调整即可:
void HeapSort()
{
for(int i=n-1; i>=0; i--)
{
swap(heap[i],heap[0]);
downAdjust(0,i-1);
}
}