堆(详解+例题)

news2024/9/24 13:16:33

一、定义

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


那么什么是二叉树呢?

如图:


二、关于小根堆实现

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


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

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

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

样例如图: 


2.2、操作

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

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

样例: 

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

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

样例: 


2.3、如何手写一个堆?

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

如图: 

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


 三、例题:

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


 图解:

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


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


思路:

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


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博客


感谢观看~ 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1534201.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C# 设置AutoScroll为true没效果的原因分析和解决办法

C#中添加tabControl 分页&#xff0c;将autoscroll设置为true发现缩小窗口没有滚动条效果。该问题出现后&#xff0c;检索发现也有很多人询问了该问题&#xff0c;但是都没有给出解决方案。 原因是内部button的属性Anchor设置为top、left、right、bottom导致的缩小界面窗口也没…

浅谈C++20 协程那点事儿

协程概念 先介绍一点协程的概念&#xff0c;如果你已经理解和掌握了相关的背景知识就可以跳过这个章节&#xff08;或者快速浏览下&#xff0c;也许我们有些观念不一致可以讨论&#xff09;。这里我想稍微聊的深入一点&#xff0c;这涉及到入门后遇到复杂的协程问题时能不能正…

代码随想录算法训练营第十六天|104.二叉树的最大深度、559.n叉树的最大深度、111.二叉树的最小深度、222.完全二叉树的节点个数

代码随想录算法训练营第十六天|104.二叉树的最大深度、559.n叉树的最大深度、111.二叉树的最小深度、222.完全二叉树的节点个数 104.二叉树的最大深度 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数…

【计算机考研】杭电 vs 浙工大 怎么选?

想求稳上岸的话&#xff0c;其他几所学校也可以考虑&#xff0c;以留在本地工作的角度考虑&#xff0c;这几所学校都能满足你的需求。 如果之后想谋求一份好工作&#xff0c;肯定优先杭电是比较稳的&#xff0c;当然复习的时候也得加把劲。 这个也可以酌情考虑&#xff0c;报…

固定资产管理系统日常业务有哪些

固定资产管理是一个非常重要的阶段。它涉及公司的长期投资、资产保值和资产报表的准确性。许多企业选择固定资产管理系统来有效管理这些资产。那么&#xff0c;固定资产管理系统的日常业务有哪些呢&#xff1f;  我们必须明确什么是固定资产管理系统。简而言之&#xff0c;它…

MYSQL报 - Lock wait timeout exceeded; try restarting transaction

前言 今天在使用数据库编辑数据时&#xff0c;页面突然卡主&#xff0c;退出程序后重新编辑&#xff0c;发现报错&#xff0c;1205 - Lock wait timeout exceeded&#xff1b; try restarting transaction&#xff08;如下图&#xff09;&#xff0c;正巧在和同事开会&#xf…

基于Spring Boot+Vue的高校学科竞赛平台

末尾获取源码作者介绍&#xff1a;大家好&#xff0c;我是墨韵&#xff0c;本人4年开发经验&#xff0c;专注定制项目开发 更多项目&#xff1a;CSDN主页YAML墨韵 学如逆水行舟&#xff0c;不进则退。学习如赶路&#xff0c;不能慢一步。 目录 一、项目简介 二、开发技术与环…

其他api的使用

其他api的使用 Frame切换frame方法认识frame 切换多窗口多窗口方法认识多窗口 切换多窗口工具封装切换多窗口工具方法认识切换多窗口工具 面试题&#xff1a;执行UI自动化时如找不到元素截图应用截图方法认识截图 图片添加的问题时间戳方法时间戳认识 验证码处理验证码方法cook…

Vulnhub靶机:Kioptrix_2014

一、介绍 运行环境&#xff1a;Virtualbox和vmware 攻击机&#xff1a;kali&#xff08;192.168.56.101&#xff09; 靶机&#xff1a;Kioptrix: 2014&#xff08;192.168.56.108&#xff09; 目标&#xff1a;获取靶机root权限和flag 靶机下载地址&#xff1a;https://ww…

【07】进阶html5

HTML5 包含两个部分的更新,分别是文档和web api 文档 HTML5 元素表 元素语义化 元素语义化是指每个 HTML 元素都代表着某种含义,在开发中应该根据元素含义选择元素 元素语义化的好处: 利于 SEO(搜索引擎优化)利于无障碍访问利于浏览器的插件分析网页新增元素 多媒体…

从零开始学习typescript系列6: typescript各种类型以及类型特殊使用

基础类型的分类 常用 boolean: 布尔值number: 支持2/8/10/16进制string: 字符串enum: 枚举类型&#xff0c;可根据value找到keyarray: 普通数组&#xff0c;有2种方式&#xff0c;string[]或者 Array<string>tuple: 特殊数组&#xff0c;指定数组里的每个元素的类型&am…

[Halcon学习笔记]标定常用的Halcon标定板规格及说明

1、介绍 大多数标定的要求都是以实心圆或方格来作为标志点&#xff0c;所以一般的标定板为棋盘格或矩阵圆点图&#xff0c;高精度的相机标定过程中&#xff0c;大多是以比较明确的特征点来作为参考&#xff0c;所以通过识别标定板的圆形&#xff0c;拟合出精确的中心位置&…

数据分析-Pandas多维数据平行坐标可视化

数据分析-Pandas多维数据平行坐标可视化 数据分析和处理中&#xff0c;难免会遇到各种数据&#xff0c;那么数据呈现怎样的规律呢&#xff1f;不管金融数据&#xff0c;风控数据&#xff0c;营销数据等等&#xff0c;莫不如此。如何通过图示展示数据的规律&#xff1f; 数据表…

Kafka Producer异步发送消息技巧大揭秘

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 Kafka Producer异步发送消息技巧大揭秘 前言异步发送概述方法实现2. producer.send(msg) 方法详解方法签名和参数说明异步发送示例代码及效果分析 3. producer.send(msg, callback) 方法解析支持事务的…

Java的类与对象

前言 Java是一门纯面向对象的语言(Object Oriented Program&#xff0c;简称OOP)&#xff0c;在面向对象的世界里&#xff0c;一切皆为对象。面向对象是解决问题的一种思想&#xff0c;主要依靠对象之间的交互完成一件事情。用面向对象的思想来涉及程序&#xff0c;更符合人们…

Java基础--集合

集合 1.可以动态的保存任意多个对象&#xff0c;使用比较方便。 2.提供了一系列方便的操作对象的方法&#xff1a;add&#xff0c;remove&#xff0c;set&#xff0c;get等。 3.使用集合添加&#xff0c;删除新元素的示意代码&#xff0c;简介明了。 集合主要是两种&#xff0…

【Web】记录巅峰极客2023 BabyURL题目复现——Jackson原生链

目录 前言 分析 EXP SignedObject打二次反序列化 打TemplatesImpl加载恶意字节码 前文&#xff1a;【Web】浅聊Jackson序列化getter的利用——POJONode 前言 题目环境:2023巅峰极客 BabyURL 之前AliyunCTF Bypassit I这题考查了这样一条链子&#xff1a; BadAttributeV…

动态规划题目练习

基础知识&#xff1a; 动态规划背包问题-CSDN博客 动态规划基础概念-CSDN博客 题目练习&#xff1a; 题目1&#xff1a;过河卒 题目描述 棋盘上 A 点有一个过河卒&#xff0c;需要走到目标 B 点。卒行走的规则&#xff1a;可以向下、或者向右。同时在棋盘上 C 点有一个对方的马…

面试算法-68-将有序数组转换为二叉搜索树

题目 给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵 平衡 二叉搜索树。 示例 1&#xff1a; 输入&#xff1a;nums [-10,-3,0,5,9] 输出&#xff1a;[0,-3,9,-10,null,5] 解释&#xff1a;[0,-10,5,null,-3,null,9] 也将被视…

力扣---子集---回溯(子集型回溯)---递归

递归法思路&#xff1a; 首先考虑为什么能用递归&#xff08;因为存在大问题和小问题之间的关系&#xff0c;大问题&#xff1a;从第 i 个数字到最后一个数字之间找子集&#xff0c;小问题&#xff1a;从第 i1 个数字到最后一个数字之间找子集&#xff09;。其次&#xff0c;用…