第一次写博客 好紧张~ 初次学习 欢迎交流~
文章目录
- 对堆的基础理解
- 堆的几个操作
- 插入一个数x
- 删除任意一个元素
- 修改任意一个元素
- 求小根堆里的最小值
- 总结
一、对堆的基础理解
1.堆是一颗完全二叉树。
2.小根堆:父结点的值<=其子节点的值
3.大根堆:父结点的值>=其子节点的值
4.对结点的左右儿子和父亲进行编号:结点i的左儿子是2*i 结点i的右儿子是2*i+1 结点i的父结点是i/2
5.堆(完全二叉树)可以用一维数组存储,如下图
二、堆的几个操作
1.插入一个值为x的元素
core idea:把新元素从堆尾插入,再逐层上浮到合适位置
代码如下(示例):
插入值为x的数
int a[1000010],size;
void up(int u)//上浮
{
if(u/2&&a[u/2]>a[u])
{
swap(a[u],a[u/2]);
up(u/2);
}
}
void push(int x)//插入
{
++size;
a[size]=x;
up(size);
}
a为存储堆的一维数组。
size为数组的长度,因为要从堆尾(数组尾)插入x,所以肯定得扩大数组长度;下标从1开始,size也是数组尾元素的下标。
u为准备上浮的结点的下标。
如果准备上浮的结点值<其父结点的值,则上浮,即交换两者的值。值得注意的是交换不只是单纯的值交换了,下标也交换了,所以这个准备上浮的结点的下标为u/2。我认为具体原因是数组的下标总是1,2,3,4,5,6……,但对应数组的值是可以交换的。
时间复杂度为O(logn)
2.删除任意一个元素
core idea:用堆的最后一个元素覆盖住要删除的这个元素,再逐层下沉/上浮到合适位置(以满足小根堆的性质)
代码如下(示例):
删除下标为k的元素
int a[1000010],size;
void up(int u)//上浮
{
if(u/2&&a[u/2]>a[u])
{
swap(a[u],a[u/2]);
up(u/2);
}
}
void down(int u)//下沉
{
int v=u;
if(u*2<=size&&a[u*2]<a[v])v=u*2+1;
if(u*2+1<=size&&a[u*2+1]<a[v])v=u*2+1;
if(u!=v)swap(a[u],a[v]),down(v);
}
void pop(int k)//删除
{
if(a[k]<=a[size]){
a[k]=a[size];
size--;
down(k);
}
else
{a[k]=a[size];
size--;
up(k);
}
}
size--:因为让最后一个元素覆盖住下标为k的元素,所以该元素相当于被删除 整个堆的大小减一
如果替代后该位置的元素变小 为了满足小根堆的性质 则上浮;如果替代后该位置的元素变大 为了满足小根堆的性质 则下沉
时间复杂度为O(logn)
3.修改任意一个元素
core idea:将值赋给要修改的元素,再逐层下沉/上浮到合适位置(以满足小根堆的性质)
下标为k的元素值修改为x
int a[1000010],size;
void up(int u)//上浮
{
if(u/2&&a[u/2]>a[u])
{
swap(a[u],a[u/2]);
up(u/2);
}
}
void down(int u)//下沉
{
int v=u;
if(u*2<=size&&a[u*2]<a[v])v=u*2+1;
if(u*2+1<=size&&a[u*2+1]<a[v])v=u*2+1;
if(u!=v)swap(a[u],a[v]),down(v);
}
void modify(int k,int x)//修改
{
if(a[k]<=x){
a[k]=x;
down(k);
}
else
{a[k]=x;
up(k);
}
}
如果赋值后该位置的元素变小 为了满足小根堆的性质 则上浮;如果赋值后该位置的元素变大 为了满足小根堆的性质 则下沉
4.求小根堆里的最小值
即a[1]
小根堆每个结点都小于等于左右儿子,因此根节点是最小值
总结
建堆=>插入操作
STL的堆就是优先队列 ,代码如下:priority_queue<int,vector<int>,greater<int>>a;
以上是以小根堆为例子,大根堆原理一样!