1.堆 堆排序

第一次写博客 好紧张~ 初次学习 欢迎交流~

文章目录

  • 对堆的基础理解
  • 堆的几个操作
    • 插入一个数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;

以上是以小根堆为例子,大根堆原理一样!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值