c++堆及其应用

堆是一种数据结构,它是一种完全二叉树。堆分为最大堆和最小堆两种类型。最大堆的特点是任何一个父节点的值都大于它的子节点的值,最小堆则相反,任何一个父节点的值都小于它的子节点的值。

堆被广泛应用于排序算法,例如堆排序就采用了最大堆的性质。堆也经常用于优先队列,因为堆的特性可以很方便地获取最大或最小的元素。

下面我们就来用一道例题来观看堆的应用

输入一个长度为 n 的整数数列,从小到大输出前 m 小的数。

输入格式
第一行包含整数 n 和 m。

第二行包含 n 个整数,表示整数数列。

输出格式
共一行,包含 m 个整数,表示整数数列中前 m 小的数。

数据范围
1≤m≤n≤10^5
1≤数列中元素≤10^9

1、暴力做法

如果用简单的做法,那么用先用一个冒泡排序来做就可以了

但是这样做法的话像上面给出的数据的话,那么也会超时

2、用之前学过的单调队列

3、今天,我们用一个小根堆来实现

首先,什么是小根堆呢

在一个数据结构中,储存类型是一棵树,它的父节点有两个儿子,左儿子和右儿子,父节点的数值大小一定比两个儿子的数值小,两个儿子的大小不定,它们的祖宗节点的数值是最小的,所以我们可以利用这个特点来完成我们今天这道题目

输入样例

5 3
4 5 1 3 2

输出样例

1 2 3

先给出具体代码再仔细分析

#include<iostream>
#include<algorithm>

using namespace std;

const int N=100010;

int q[N];
int n,m,x;

void down(int u)
{
    int t=u;
    if(u*2<=x&&q[u*2]<q[t]) t=u*2;
    if(u*2+1<=x&&q[u*2+1]<q[t]) t=u*2+1;
    if(u!=t)
    {
        swap(q[u],q[t]);
        down(t);
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    
    for(int i=1;i<=n;i++) scanf("%d",&q[i]);
    x=n;
    
    for(int i=n/2;i;i--) down(i);
    
    while(m--) 
    {
        printf("%d ",q[1]);
        q[1]=q[x];
        x--;
        down(1);
    }
    
    return 0;
}

1、我们先开比数据多十个类型的数组,是为了防止数据溢出

2、down函数

想一下,如果我们取出了第一个最小的数,那我们怎么得到第二小的数值呢?我们先让该堆中的最后一个元素变为堆顶元素,然后让该元素和它的左儿子和右儿子比较大小,比较的顺序是任意的,如果堆顶元素比它的儿子数值更大,就交换,然后再让该元素和它的左,右儿子比较大小,直至不能比较为止

这时取得了堆顶元素之后,要把该元素删除掉,然后再和最后一个元素交换,再删除,这样就能得到第二小数值的数,重复m次,就能得到n个数中前m个小的数了

3、怎么得到一个小根堆

我认为从n/2开始,还有一个角度可以理解,因为n是最大值,n/2是n的父节点,因为n是最大,所以n/2是最大的有子节点的父节点,所以从n/2往前遍历,就可以把整个数组遍历一遍,这样也能减少时间复杂度

注意,在得到整个数组之后,让x=n,这样就是n就是最大值

最后欢迎大家来acwing学习

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值