当前位置: 首页 > news >正文

【algorithm】算法学习----堆

堆涉及到的五个问题:

  • 插入一个数

  • 求集合中的最小值

  • 删除最小值

  • 插入任意一个元素

  • 删除任意一个元素

对于堆,这里需要使用树来存储。

这里以小根堆来举例:

什么是小根堆,小根堆就是父节点比其两个孩子节点都要小。

由此我们可以想到根节点就是当前堆的最小值。

堆其实是一个完全二叉树。

什么是完全二叉树?

就是除了最后一层,其余层都是满度(都是2),并且最后一层的是从左到右排列
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cuRez63D-1662215980468)(D:\MarkdownImages\2022-09-01-21-44-12-image.png)]
但是如果使用节点point+value比较麻烦并且不适用于算法题里面。

所以我们使用一维数组来存储:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bC9VgIQH-1662215980469)(D:\MarkdownImages\2022-09-03-10-57-48-image.png)]
因此我们对于这个完全二叉树每次插入一个节点就有两个操作down()up()操作。

要么网上调,要么往下挑。

down的逻辑:<还是以小根堆为例>

如果当前的节点比其两个孩子的节点都要大,因此将该节点与其孩子节点中最小的进行交换。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q22ypat9-1662215980469)(D:\MarkdownImages\2022-09-03-19-20-26-image.png)]
up的逻辑与:

如果当前的节点比其父亲节点要大,则交换这两个节点,一直到上升到合适的位置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I29Q3lQ5-1662215980470)(D:\MarkdownImages\2022-09-03-19-20-42-image.png)]
因此对于开头的五个问题可以有以下解法:

假设堆为heap[N],heap数组的大小为size.

插入一个数heap[++size]=x;up(x)
求集合中的最小值heap[1]
删除最小值使用最后一个元素覆盖根节点,然后删除最后一个元素。然后再让最后一个元素Down() heap[1]=heap[size];size–;down(1)
插入任意一个元素heap[k]=x;Down(k)/Up(k);
删除任意一个元素heap[k]=heap[size];size–;Down(k)/Up(k);

概念理解题:838. 堆排序 - AcWing题库

正式做题可能会遇到一个问题,我输入的是一个数组,我怎么把它构建成堆呢?

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1e5+10;

int h[N];
int s,m,n;

void down(int u)
{
    int t=u;
    if(2*u<=s&&h[2*u]<h[t]) t=2*u;
    if(2*u+1<=s&&h[2*u+1]<h[t]) t=2*u+1;
    if(t!=u)
    {
        swap(h[t],h[u]);
        down(t);
    }
}
int main()
{
    cin>>n>>m;

    for(int i=1;i<=n;i++) cin>>h[i];

    s=n;
//这里就涉及到了如何构建堆:
    for(int i=n/2;i>=1;i--) down(i);

    while(m--)
    {
        cout<<h[1]<<" ";
        h[1]=h[s];
        s--;
        down(1);
    }
    return 0;
}
//Down()函数
void down(int u)
{
    int t=u;
    if(2*u<=s&&h[2*u]<h[t]) t=2*u;
    if(2*u+1<=s&&h[2*u+1]<h[t]) t=2*u+1;
    if(t!=u)
    {
        swap(h[t],h[u]);
        down(t);
    }
}
//Up()函数
void up(int u)
{
    while(u/2&&h[u/2]>h[u])
    swap(h[u/2],h[u]);
    u/=2;
}

练习题:839. 模拟堆 - AcWing题库

其实我们仔细想啊,堆节点的下标我们是知道的,我们只需要一个数组存储下标为i的数是第j次插入的。但是看一下这道题目的要求:

  1. I x,插入一个数 x;
  2. PM,输出当前集合中的最小值;
  3. DM,删除当前集合中的最小值(数据保证此时的最小值唯一);
  4. D k,删除第 k 个插入的数;
  5. C k x,修改第 k 个插入的数,将其变为 x;

知道第k个插入的数我们还得找到对应数的下标,因此我们有了ph与hp这两个互指指针。

ph[k]表示第k个插入的数在数组的下标什么。

hp[k]表示堆里下标为k的点是第几个被插入的
在这里插入图片描述


#include <iostream>
#include <algorithm>
#include <string.h>

using namespace std;

const int N = 100010;

int h[N], ph[N], hp[N], cnt;

void heap_swap(int a, int b)
{
    swap(ph[hp[a]],ph[hp[b]]);
    swap(hp[a], hp[b]);
    swap(h[a], h[b]);
}

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

void up(int u)
{
    while (u / 2 && h[u] < h[u / 2])
    {
        heap_swap(u, u / 2);
        u >>= 1;
    }
}

int main()
{
    int n, m = 0;
    scanf("%d", &n);
    while (n -- )
    {
        char op[5];
        int k, x;
        scanf("%s", op);
        if (!strcmp(op, "I"))
        {
            scanf("%d", &x);
            cnt ++ ;
            m ++ ;
            ph[m] = cnt, hp[cnt] = m;
            h[cnt] = x;
            up(cnt);
        }
        else if (!strcmp(op, "PM")) printf("%d\n", h[1]);
        else if (!strcmp(op, "DM"))
        {
            heap_swap(1, cnt);
            cnt -- ;
            down(1);
        }
        else if (!strcmp(op, "D"))
        {
            scanf("%d", &k);
            k = ph[k];
            heap_swap(k, cnt);
            cnt -- ;
            up(k);
            down(k);
        }
        else
        {
            scanf("%d%d", &k, &x);
            k = ph[k];
            h[k] = x;
            up(k);
            down(k);
        }
    }

    return 0;
}

补充

strcmp()函数

该函数返回值如下:

  • 如果返回值小于 0,则表示 str1 小于 str2。
  • 如果返回值大于 0,则表示 str1 大于 str2。s
  • 如果返回值等于 0,则表示 str1 等于 str2。

相关文章:

  • Q_ENUM Q_ENUMS Q_ENUM_NS Q_FLAG Q_FLAGS Q_FLAG_NS
  • 国外5G行业应用产业政策分析及对我国的启示
  • 【C语言】文件输入输出操作
  • 【教3妹学算法-每日一题】竞赛题:6171. 和相等的子数组
  • 遗传算法GA求解非连续函数问题
  • 【电商营销】为什么需要从获取客户转向留住客户
  • 实战讲解Redis基础数据类型List增删改查(带Java源码)
  • 分布式消息队列RocketMQ介绍
  • Django视图层模版层全面解析全网最细的教程
  • java Map集合基本概念
  • 类与对象以及原型机制
  • IMX6ULL学习笔记(6)——通过USB OTG烧录U-Boot(MfgTool工具)
  • 牛客 NC25005 [USACO 2008 Ope S]Clear And Present Danger
  • 洛谷 P2349:金字塔 ← 链式前向星 dfs
  • Flink—窗口、时间和水印
  • CSS中外联样式表代表的含义
  • es6--symbol
  • leetcode46 Permutation 排列组合
  • opencv python Meanshift 和 Camshift
  • PHP那些事儿
  • React 快速上手 - 06 容器组件、展示组件、操作组件
  • Web设计流程优化:网页效果图设计新思路
  • weex踩坑之旅第一弹 ~ 搭建具有入口文件的weex脚手架
  • 工作手记之html2canvas使用概述
  • 欢迎参加第二届中国游戏开发者大会
  • 基于axios的vue插件,让http请求更简单
  • 如何用vue打造一个移动端音乐播放器
  • 深入浅出Node.js
  • 使用 Xcode 的 Target 区分开发和生产环境
  • 一份游戏开发学习路线
  • 用quicker-worker.js轻松跑一个大数据遍历
  • 原生 js 实现移动端 Touch 滑动反弹
  • [地铁译]使用SSD缓存应用数据——Moneta项目: 低成本优化的下一代EVCache ...
  • mysql 慢查询分析工具:pt-query-digest 在mac 上的安装使用 ...
  • 好程序员web前端教程分享CSS不同元素margin的计算 ...
  • 湖北分布式智能数据采集方法有哪些?
  • ​第20课 在Android Native开发中加入新的C++类
  • #调用传感器数据_Flink使用函数之监控传感器温度上升提醒
  • #多叉树深度遍历_结合深度学习的视频编码方法--帧内预测
  • #绘制圆心_R语言——绘制一个诚意满满的圆 祝你2021圆圆满满
  • (2)MFC+openGL单文档框架glFrame
  • (MIT博士)林达华老师-概率模型与计算机视觉”
  • (六)c52学习之旅-独立按键
  • (五)网络优化与超参数选择--九五小庞
  • (转)Android学习系列(31)--App自动化之使用Ant编译项目多渠道打包
  • (转)大型网站的系统架构
  • *** 2003
  • .Net Attribute详解(上)-Attribute本质以及一个简单示例
  • .net framework 4.0中如何 输出 form 的name属性。
  • .NET LINQ 通常分 Syntax Query 和Syntax Method
  • .Net Redis的秒杀Dome和异步执行
  • .NET开发人员必知的八个网站
  • .Net中的设计模式——Factory Method模式
  • .NET中两种OCR方式对比
  • @private @protected @public