首页 > 其他分享 >模拟堆(详解+例题)

模拟堆(详解+例题)

时间:2024-03-23 16:30:54浏览次数:23  
标签:sz 下标 int down -- 详解 swap 例题 模拟

一、定义

维护一个数据集合,堆是一个完全二叉树。


那么什么是二叉树呢?

如图:ebb910b87cae4c698f49a9eb76fc14b5.png


二、关于小根堆实现

98d6a4f12d914ceabbdd0012629c2fa2.png

性质:每个根节点都小于等于左右两边,所以树根为最小值。 


2.1、堆存储(用一维数组来存)

bf65d851a0664d35a8bcf62691257dba.png

 记住规则:x(根)的左儿子 = x * 2;

                   x(根)的右儿子 = x * 2 + 1

样例如图: 

dfdb5dd1f8594749948cda228def91ab.png


2.2、操作

2.2.1、操作1、down(x) :节点下移

如果把一个值变大了,就让他往下移动。

样例: 

471bee5173a740a7a7e122f132bdfed7.png


2.2.2、操作2、 up(x):节点向上移

如果把某一个值变小了,就让他向上移动

样例: 

bdb1d592caa5409288493505bae3bb3d.png


2.3、如何手写一个堆?

注意:下标要从1开始,heap[]堆,size:堆长度

如图: 

849e092dacfd4f888d45cb9a524c68a7.png

这里后面会有对应的例题,详细解释4、5操作 


 三、例题:

3.1、堆排序 :此题来源于acwing

5a2c41393a6c4e7faa4086f53a10ad1a.png


 图解:

70236ccf14a34959a435887e82dc5a3e.png

1d2e13b0cde0413595169d7977eee8ad.png 上述两个图来自于B站董晓算法的视频


代码模板: 
//下沉
void down(int u)
{
    int t = u;
    if(u*2 <= sz && h[u*2] < h[t]) t = u * 2;
    if(u*2+1 <= sz && h[u*2+1] < h[t]) t = u * 2 + 1;
    if(t != u)
    {
        heap_swap(t,u); //由于后面需要找到第k的数,所以不能用普通的交换,需要用我们手写的交换函数
        down(t);//下沉,直到不满足位置
    }
}
//上浮
void up(int u)
{
    //与根部比较,如果父亲大于儿子就需要上浮
    while(u/2 && h[u/2] > h[u]){
        heap_swap(u/2, u);
        u = u/2;
    }
}

AC代码:
#include<iostream>
#include<algorithm>

using namespace std;

const int N = 1e5+10;
int h[N],sz;
int n,m;

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

int main()
{
    scanf("%d %d", &n, &m);
    for(int i=1;i<=n;i++) scanf("%d ",&h[i]);
    sz = n;
    //构建堆,从n/2序号开始创建,把最小值挪到树根,相当于忽略最后一层
    for(int i=n/2;i>=1;i--)
    {
        down(i);
    }
    while (m -- )
    {
        printf("%d ",h[1]);
        h[1] = h[sz];
        sz--;
        down(1);
    }
    return 0;
}

#include<iostream>
#include<algorithm>

using namespace std;

const int N = 1e5+10;
int h[N],sz;
int n,m;
//下沉
void down(int u)
{
    int t = u;
    //根据小根堆性质,保证父亲节点是小于左右两个儿子的
    if(u*2 <= sz && h[u*2] < h[t]) t = u * 2;
    if(u*2+1 <= sz && h[u*2+1] < h[t]) t = u * 2 + 1;
    if(t != u)
    {
        swap(h[t],h[u]);//如果不同,需要交换两个节点
        down(t);//继续下沉
    }
}

int main()
{
    scanf("%d %d", &n, &m);
    for(int i=1;i<=n;i++) scanf("%d ",&h[i]);
    sz = n;//记得传一下长度给sz。
    //构建堆,从n/2序号开始创建,把最小值挪到树根,相当于忽略最后一层
    for(int i=n/2;i>=1;i--)
    {
        //这里非常巧妙,从后面开始去下沉,等到树根的时候,会第一个开始down,
        //会排好,不用担心乱序
        down(i);
    }
    while (m -- )
    {
        printf("%d ",h[1]);//取出最小值
        h[1] = h[sz]; //根据规则操作后续步骤
        sz--;
        down(1);
    }
    return 0;
}

3.2、模拟堆:此题同样来源于acwing

292b65b88d114511a3f94116ae743e8a.png4f98294c55924799b0a293a5893e1a2f.png


思路:

由于,此题需要记录一下第k个插入的数,所以需要用两个映射的数组去维护一下第k个插入的数,和当前堆中元素的下标,此处附上一位acwing评论区的一位大佬的讲解,我觉得讲的非常好,可以帮助此题理解两个数组的含义,建议先看代码,再看这个。代码中有详细注释,如果有错误欢迎指出~。

73ecd3a1ddcc41dea1f81ba5f441e919.png


AC代码: 
#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

const int N = 1e5+10;
//h[]用来存堆,ph[]用来存第k个数对应的堆里的下标
//hp[]用来存堆下标下是第几个存入的数
int h[N],ph[N],hp[N],sz;
int n,m;

void heap_swap(int a,int b)
{
    swap(h[a],h[b]);//交换堆中的两个数值
    swap(hp[a],hp[b]);//在堆中对应的下标下是第几个存入的数,交换一下
    swap(ph[hp[a]],ph[hp[b]]);//交换一下堆中的下标
}
//下沉
void down(int u)
{
    int t = u;
    if(u*2 <= sz && h[u*2] < h[t]) t = u * 2;
    if(u*2+1 <= sz && h[u*2+1] < h[t]) t = u * 2 + 1;
    if(t != u)
    {
        heap_swap(t,u); //由于后面需要找到第k的数,所以不能用普通的交换,需要用我们手写的交换函数
        down(t);//下沉,直到不满足位置
    }
}
//上浮
void up(int u)
{
    //与根部比较,如果父亲大于儿子就需要上浮
    while(u/2 && h[u/2] > h[u]){
        heap_swap(u/2, u);
        u = u/2;
    }
}

int main()
{
    scanf("%d", &n);
    while (n -- )
    {
        char op[10];//根据题目要求输入字符串
        scanf("%s", op);
        if(!strcmp(op,"I"))
        {
            int x;
            scanf("%d", &x);
            m++;//第几个插入的
            sz++;//当前下标
            //ph表示第k插入的数在堆中的下标是多少
            //hp表示该数堆中下标对应第几个插入的数
            ph[m] = sz,hp[sz] = m;
            h[sz] = x;//堆中存入x
            up(sz);//上浮
        }
        else if(!strcmp(op,"PM")) //找到最小的数,就是树根
        {
            printf("%d\n",h[1]);
        }
        else if(!strcmp(op,"DM")) //注意交换后sz--;
        {
            heap_swap(1,sz);
            sz--;
            down(1);
        }
        else if(!strcmp(op,"D")) //删除第k个数
        {
            int k;
            scanf("%d", &k);
            k = ph[k]; //找到第k个数下的堆中的元素下标
            heap_swap(k,sz);
            sz--;
            down(k),up(k);//down和up只会执行一个操作,因为要么大,要么小
        }
        else
        {
            int k,x;
            scanf("%d %d", &k,&x);
            k = ph[k];
            h[k] = x;
            down(k),up(k);//同理
        }
    }
    return 0;
}

看会以上,大家可以去做一下这个题: 

P3378 【模板】堆 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题解在这里:

P3378 【模板】堆-CSDN博客


感谢观看~ 

标签:sz,下标,int,down,--,详解,swap,例题,模拟
From: https://blog.csdn.net/m0_67717626/article/details/136918180

相关文章

  • 分布式详解
    文章目录概述分布式开发优点和缺点分布式存在的作用分布式和集群的区别集群的特点BASE理论BASE理论的三要素CAP理论二段式满足cap理论的哪两个理论分析下分布式强一致性、弱一致性、最终一致性衡量分布式系统的指标分布式下down机的处理⽅案分布式系统设计paxos和raft......
  • C语言字符函数和字符串函数及内存函数详解(干货小知识:常用函数的模拟实现)
    文章目录1.字符函数1.1字符分类函数1.2字符转换函数2.字符串函数2.1strlen函数2.1.1strlen函数的使用:2.1.2strlen函数的模拟实现2.2strcpy函数2.2.1strcpy函数的使用2.2.2strcpy函数的模拟实现2.3strcat函数2.3.1strcat函数的使用2.3.2strcat函数的模拟实......
  • Redis基础命令集详解
    目录1.Redis基础命令2.Redis的经典案例2.1缓存2.2计数器2.3发布订阅Redis是一个开源、内存存储的数据结构服务器,它支持多种数据结构,如字符串、哈希表、列表、集合、有序集合等。在Redis中,使用一些基础的命令来操作这些数据结构。1.Redis基础命令下面是一些常用的R......
  • STM32之HAL开发——启动文件详解【精华版】
    启动文件介绍启动文件是使用机器认识的汇编语言,由汇编编写,是系统上电复位后第一个执行的程序,经过一些必要的配置,最终能够调用main函数,使得用户程序能够在MCU上正常运行起来的必备文件。无论是是何种MCU,从简单的51,MSP430,到ARM9,ARM11,A7都必须有启动文件,因为对于嵌入式......
  • 0基础学习C语言第一章:常量与变量详解
    一、常量定义:在程序运行过程中,其值不能被改变的量称为常量。常用常量有以下几类:1.整型常量十进制整数形式例如:234,-1232.实型(浮点型)常量十进制小数形式:由数字、小数点组成例如:2.345、-23.345指数形式:如:1.23e2(相当于1.23x10²)由于在计算机输入输出时,无法表示上角......
  • 数据结构(九)模拟堆---以题为例
    维护一个集合,初始时集合为空,支持如下几种操作:Ix,插入一个数 x;PM,输出当前集合中的最小值;DM,删除当前集合中的最小值(数据保证此时的最小值唯一);Dk,删除第 k 个插入的数;Ckx,修改第 k 个插入的数,将其变为 x;现在要进行 N 次操作,对于所有第 2 个操作,输出当前集合的最小......
  • 数据结构:详解【栈和队列】的实现
    目录1.栈1.1栈的概念及结构1.2栈的实现1.3栈的功能1.4栈的功能的实现1.5完整代码2.队列2.1队列的概念及结构2.2队列的实现2.3队列的功能2.4队列的功能的实现2.5完整代码1.栈1.1栈的概念及结构栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除......
  • iOS模拟器 Unable to boot the Simulator —— Ficow笔记
     本文首发于FicowShen'sBlog,原文地址:iOS模拟器UnabletoboottheSimulator——Ficow笔记。内容概览前言终结模拟器进程命令行改权限清除模拟器缓存总结 前言 iOS模拟器和Xcode一样不靠谱,问题也不少。......
  • java方法详解
    java方法详解方法是语句的集合。目的是解决一类问题。一个方法只完成一个功能,这样有利于后期的扩展。(单一职责原理)java都是值传递!有一个值copy的过程。publicclassDemo02{publicstaticvoidmain(String[]args){intmax=max(10,20);System.......
  • 计算机/网安 面试例题(四)
    一、Web常问1.SQL注入原理的种类?防御呢?预编译原理?原理:在数据交互中,前端的数据传入到后台处理时,由于后端没有做严格的判断,导致其传入的“数据”拼接到SQL语句中后,被当作SQL语句的一部分执行。种类:字符,数字,布尔,报错,延迟,联合,堆叠,宽字节,XFF等修复:使用预编译,PDO,正则表......