Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >算法与数据结构之最大/最小堆

算法与数据结构之最大/最小堆

作者头像
灯珑LoGin
发布于 2022-10-31 02:42:29
发布于 2022-10-31 02:42:29
1.3K00
代码可运行
举报
文章被收录于专栏:龙进的专栏龙进的专栏
运行总次数:0
代码可运行

这里涉及到了堆结构,作为引入,要先讲讲一种特殊的树结构——完全二叉树

完全二叉树

完全二叉树就是像下图一样的二叉树,所有叶结点的深度相同,并且所有内部结点都有两个子结点

看这个图就很好理解什么叫完全二叉树了。真的很完美啊哈哈哈。

但是,有另外一种树结构也叫做完全二叉树。准确来说就是近似是完全二叉树。

长这样子:

上面这种二叉树,叶节点的深度的最大差距是1,最下层叶节点都集中分布在这层最左边的若干位置上,那么这种二叉树我们也可以把它叫做完全二叉树。

二叉堆

上面这种类型的数据结构我们将它称为二叉堆。二叉堆在逻辑上虽然是完全二叉树,但是它却是以1为起点的一维数组表示的。假设表示二叉堆的数组为A,二叉堆的大小(元素的数量)为H,那么二叉堆的元素就存储在 A[1, … , H] 中,其中根的下标是1.观察可以发现,只要一个结点的下标i确定了,那么它的父节点就是floor(i/2),左子结点下标是2*i, 右子结点的下标是2*i+1。

二叉堆和完全二叉树的区别之一在于,二叉堆中存储的各结点的键值需要保证堆具有以下性质之一

·最大堆性质: 结点的键值都小于等于其父结点的键值。

·最小堆性质: 结点的键值都大于等于其父结点的键值。

满足最大堆性质的二叉堆叫做最大堆,满足最小堆性质的二叉堆叫做最小堆。

最大堆的根结点中存储着最大的元素,最小堆的根结点中存储着最小的元素。

需要注意的是,二叉堆中只有父子结点之间有大小关系的限制,而兄弟结点之间并没有大小关系的限制。

应用二叉堆这种数据结构,我们就可以在保持数据大小关系的前提下,有效地取出优先级最高的元素,或者添加新的元素。

构建完全二叉树

下面要实现一个程序,读取以完全二叉树形式表示的二叉堆。注意,这里只是构建一棵完全二叉树,并不需要这个树满足二叉堆的规则。

输入两行数据,第一行是元素的个数

第二行是各个结点的值。

要求按结点id顺次输出结点的父结点、左子结点、右子结点的值。如果这些结点不存在,那么就不用输出。

题目出自

http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=ALDS1_9_A

题目编号是 ALDS1_9_A

样例输入

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
5
7 8 1 2 3

样例输出

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
node 1: key = 7, left key = 8, right key = 1, 
node 2: key = 8, parent key = 7, left key = 2, right key = 3, 
node 3: key = 1, parent key = 7, 
node 4: key = 2, parent key = 8, 
node 5: key = 3, parent key = 8, 

这道题我们只需要根据完全二叉树的定义,就能生成这棵完全二叉树。

完全二叉树的代码实现

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include<iostream>
#include<cstdio>
#include<math.h>
using namespace std;


int main()
{
    long long nd[260];
    int h;
    cin>>h;
    for(int i=1;i<=h;i++)
    {
        cin>>nd[i];
    }

    for(int i=1;i<=h;i++)
    {
        printf("node %d: key = %lld, ",i, nd[i]);
        if(floor(i/2)!=0)
            printf("parent key = %lld, ", nd[int(floor(i/2))]);
        if(2*i<=h)
            printf("left key = %lld, ", nd[2*i]);
        if(2*i+1<=h)
            printf("right key = %lld, ", nd[2*i+1]);

        cout<<endl;

    }
}

最大堆

有了上面生成完全二叉树的基础,我们就能根据最大堆的性质来生成最大堆。

最大堆的生成有一个叫做堆化的过程,在这个过程中,利用自己写的一个max_Heapify函数,使得 A[i] 的值一致向叶结点方向移动,直至它找到合适的位置。这个过程能通过递归来实现。

但是如果从完全二叉树的根入手来解决这个问题的话,就会发现整个操作变得异常的复杂。

所以,我们可以从叶结点来入手解决这个问题。

首先,我们会发现叶结点由于没有子结点,根本不需要向下进行任何的对换操作。那么我们就直接研究叶结点的父结点就好了。

由于完全二叉树每一层的结点数量最大是上一层的两倍,所以,我们只需要从结点id为H/2的结点开始,终点是结点id=1的结点,都进行一遍max_Heapify就可以生成最大堆了。

每次执行max_Heapify的时候,当前结点i的左右子树都已经是最大堆了,所以,我们只需要关注当前结点该移动到哪个位置而已。

生成最大堆的代码实现

题目来源:

http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=ALDS1_9_B

在这个程序中,输入数据的格式和上一题一样

我们要实现把一棵完全二叉树变成一个最大堆。那就按照上面的思路,从叶结点入手,自下而上的处理

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include<iostream>
using namespace std;

long long nd[500009];
int h;

void max_Heapify(int i)
{
    int l = 2*i;
    int r = 2*i+1;

    //从左子结点、自身、右子结点中选出值最大的结点

    int largest = i;
    if(l<=h&&nd[l]>nd[largest])
        largest = l;
    if(r<=h&&nd[r]>nd[largest])
        largest = r;

    if(largest!=i)
    {
        swap(nd[i], nd[largest]);
        max_Heapify(largest);
    }

}

int main()
{
    cin>>h;
    for(int i=1;i<=h;i++)
    {
        cin>>nd[i];
    }

    for(int i=h/2;i>=1;i--)
        max_Heapify(i);

    for(int i=1;i<=h;i++)
        cout<<" "<<nd[i];
    cout<<endl;
}

生成最小堆

我们只需要把上面的生成最大堆的代码稍加修改,就能改成生成最小堆的代码。

只要把max_Heapify函数改成下面的min_Heapify函数就可以了。其实就是改变了交换元素的规则。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void min_Heapify(int i)
{
    int l = 2*i;
    int r = 2*i+1;

    //从左子结点、自身、右子结点中选出值最小的结点

    int smallest = i;
    if(l<=h&&nd[l]<nd[smallest])
        smallest = l;
    if(r<=h&&nd[r]<nd[smallest])
        smallest = r;

    if(smallest!=i)
    {
        swap(nd[i], nd[smallest]);
        min_Heapify(smallest);
    }

}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020年11月18日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
学习笔记 | 《算法导论》之从入门到放弃(3)
算法导论打卡3,主要内容:堆排序 第六章 堆排序 堆 二叉堆是一个数组,它可以被看成一个近似的完全二叉树。树上的每一个结点对应数组中的一个元素。除了最底层外,该树是完全充满的,而且是从左向右填充。 表示堆的数组A包括两个属性:A.length(通常)给出数组元素的个数,A.heap-size表示有多少个堆元素存储在该数组中。也就是说虽然A[1..A.length]可能都存有数据,但只有A[1..A.heap-size]中存放的是堆的有效元素,这里0≤A.heap-size≤A.length。树的根节点是A[
Justlovesmile
2021/12/14
2160
学习笔记 | 《算法导论》之从入门到放弃(3)
算法导论第六章堆排序(一)
现在来看, 堆的含义大概有两种,一种是数据结构,一种是在一些语言中所定义的“垃圾回收机制”,如Java,在书本上的开篇强调了这两者,并强调若非特殊说明,皆把堆看做是一种数据结构。 (二叉)堆的定义: 1)它是一个数组,可以被看成是一棵近似的完全二叉树,树上的每一个节点看做是数组中的每一个元素。 2)堆分为最大堆和最小堆,最大堆中每一棵子树的父节点的值大于孩子节点,最小堆则相反。 3)表示堆的数组A包括两个属性:A.length和A.heap_size。前者是数组元素的个数,后者是堆元素的个数,heap_si
Linux云计算网络
2018/01/11
7812
【42期】盘点那些必问的数据结构算法题之二叉堆
来自:juejin.im/post/5b9e6341e51d450e51626374
良月柒
2020/09/23
3780
【42期】盘点那些必问的数据结构算法题之二叉堆
再谈堆排序:堆排序算法流程步骤透解—最大堆构建原理
Heapsort类似于 选择排序我们反复选择最大的项目并将其移动到列表的末尾。主要的区别在于,我们不是扫描整个列表来查找最大的项目,而是将列表转换为最大堆(父节点的值总是大于子节点,反之最小堆)以加快速度。
周陆军博客
2023/06/06
5810
算法与数据结构之优先级队列
复习一下:前面讲的最大最小堆的生成,是把一个数组转换成完全二叉树之后,才转换成最大最小堆的。然后生成的时候先从最下方的叶结点开始生成最大/最小堆。
灯珑LoGin
2022/10/31
2480
算法与数据结构之优先级队列
如醉如痴之最小堆
一道简单的题,可以让你如醉如痴,更是因为这一道题,你才会学会很多,不要小看简单,简单中蕴含深意。
公众号guangcity
2019/09/20
3750
如醉如痴之最小堆
Java常见排序算法详解——堆排序
要了解堆首先得了解一下二叉树,在计算机科学中,二叉树是每个节点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。二叉树常被用于实现二叉查找树和二叉堆。 二叉树的每个结点至多只有二棵子树(不存在度大于 2 的结点),二叉树的子树有左右之分,次序不能颠倒。二叉树的第 i 层至多有 2i - 1 个结点;深度为 k 的二叉树至多有 2k - 1 个结点;对任何一棵二叉树 T,如果其终端结点数为 n0,度为 2 的结点数为 n2,则n0 = n2 + 1。
Demo_Yang
2019/04/18
9390
Java常见排序算法详解——堆排序
数据结构与算法(4)——优先队列和堆什么是优先队列?堆和二叉堆LeetCode相关题目整理
听这个名字就能知道,优先队列也是一种队列,只不过不同的是,优先队列的出队顺序是按照优先级来的;在有些情况下,可能需要找到元素集合中的最小或者最大元素,可以利用优先队列ADT来完成操作,优先队列ADT是一种数据结构,它支持插入和删除最小值操作(返回并删除最小元素)或删除最大值操作(返回并删除最大元素);
我没有三颗心脏
2018/07/24
1.3K0
数据结构与算法(4)——优先队列和堆什么是优先队列?堆和二叉堆LeetCode相关题目整理
10亿个数字里里面找最小的10个
Top K指的是从n(很大)个数据中,选取最大(小)的k个数据。例如学校要从全校学生中找到成绩最高的500名学生,再例如某搜索引擎要统计每天的100条搜索次数最多的关键词。
用户3467126
2019/11/06
4K0
10亿个数字里里面找最小的10个
【化解数据结构】详解堆结构,并实现最小堆结构
你可能会知道在内存中有栈和堆之分,但是这里堆和内存中的堆不一样,这里的堆是一种数据存储的方式
小丞同学
2021/12/01
6280
【化解数据结构】详解堆结构,并实现最小堆结构
数据结构与算法:堆
树的结点包含一个数据元素及若干指向其子树的分支。结点拥有的子树数称为结点的度(Degree)。度为0的结点称为叶结点(Leaf)或终端结点度不为0的结点称为非终端结点或分支结点。除根结点之外,分支结点也称为内部结点。树的度是树内各结点的度的最大值。如图所示,这棵树结点的度的最大值是结点D的度为3,所以树的度为3
用户11029103
2024/03/19
3250
数据结构与算法:堆
Python 一网打尽<排序算法>之堆排序算法中的树
树是最基本的数据结构,可以用树映射现实世界中一对多的群体关系。如公司的组织结构、网页中标签之间的关系、操作系统中文件与目录结构……都是用树结构描述的。
一枚大果壳
2022/08/23
6490
Python  一网打尽<排序算法>之堆排序算法中的树
[算法与数据结构] 《算法导论》堆排序笔记
堆排序的实现是靠叫做“堆”的数据结构来实现的。所以学习堆排序,首先要了解什么是堆 堆 堆是一个数组,每个结点表示数组中的一个元素,堆可以看做是一个近似的完全二叉树。完全二叉树是所有叶结点深度相同,且所有内部结点度为2的2叉树。 树的高度:从结点x向下到某个叶结点最长简单路径中边的条数 表示堆的数组A包括两个属性:A.length给出数组元素的个数,A.heap-size表示有多少个堆元素存储在该数组中。 最大堆和最小堆 最大堆:除了根以外的所有结点i都要满足 A[PARENT(i)] >= A[i] 意思是
用户1622570
2018/04/12
8750
[算法与数据结构] 《算法导论》堆排序笔记
【学点数据结构和算法】06-二叉堆和优先队列
写在前面: 博主是一名大数据的初学者,昵称来源于《爱丽丝梦游仙境》中的Alice和自己的昵称。作为一名互联网小白,写博客一方面是为了记录自己的学习历程,一方面是希望能够帮助到很多和自己一样处于起步阶段的萌新。由于水平有限,博客中难免会有一些错误,有纰漏之处恳请各位大佬不吝赐教!个人小站:http://alices.ibilibili.xyz/ , 博客主页:https://alice.blog.csdn.net/ 尽管当前水平可能不及各位大佬,但我还是希望自己能够做得更好,因为一天的生活就是一生的缩影。
大数据梦想家
2021/01/27
3810
【学点数据结构和算法】06-二叉堆和优先队列
堆排序与优先队列
(二叉)堆物理上是一个数组,逻辑上是一棵完全二叉树,树上的每一个结点对应数组中的一个元素。 设表示堆的数组 ,其包含两个属性:
hotarugali
2022/03/01
3330
数据结构之树
树(Tree)是一种抽象数据类型(ADT)或是实现这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合。它是由n(n>0)个有限节点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的圣诞树,也就是说它是根朝上,而叶朝下的。它具有以下的特点:
我是攻城师
2018/11/08
8560
数据结构基础-优先队列和堆
优先队列可以看做队列的一种,区别在于,在优先队列中,元素进入队列的顺序可能与其被操作的顺序不同。他支持插入(Insert)和删除最小值(DeleteMin)操作(返回并删除最小元素)或删除最大值(DeleteMax)操作(返回并删除最大元素)。
1025645
2019/03/04
3760
数据结构基础-优先队列和堆
我们在很多情况下都听到“堆”这个计算机术语,那么“堆”到底是什么呢?在数据结构中,堆是一种数据结构,具体一点,最常用的堆就是二叉堆, 二叉堆就是一棵完全二叉树(以下简称堆),我们可以利用这种数据结构来完成一些任务,典型的例子:堆排序就是利用堆来实现的一种高效的排序方式。接下来我们先看一下什么是完全二叉树:
指点
2019/01/18
6270
堆
信息竞赛进阶指南--二叉堆(模板)
啥是二叉堆 二叉堆是一种特殊的堆,二叉堆是完全二元树(二叉树)或者是近似完全二元树(二叉树)。二叉堆有两种:最大堆和最小堆。最大堆:父结点的键值总是大于或等于任何一个子节点的键值;最小堆:父结点的键值总是小于或等于任何一个子节点的键值。
风骨散人Chiam
2020/10/28
6670
数据结构与算法-关于堆的基本存储介绍
堆是一种特殊的树形数据结构,常用于实现优先队列。堆通常以完全二叉树的形式存储在数组中,这样可以高效地访问父节点、子节点以及兄弟节点。本文将深入探讨堆的基本存储原理,包括最大堆和最小堆的概念,并通过具体的案例代码详细说明堆的实现和操作。
用户11147438
2024/08/09
1780
数据结构与算法-关于堆的基本存储介绍
相关推荐
学习笔记 | 《算法导论》之从入门到放弃(3)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验