【数据结构】超详细——堆的实现

news2024/9/25 19:21:50

一、堆的概念及性质

1.1 什么是堆?

  堆是一种完全二叉树(具体在下一章讲述),若二叉树的深度h,除了第h层外其余各层节点数满了,只有第h层缺额且该层结点靠左;任何一个数组可以看作完全二叉树,但不一定是堆。

  此外,堆可以分为大根堆和小根堆。根是堆里最大数的完全二叉树称为大根堆,根是堆里最小数的完全二叉树称为小根堆

image-20230121212608359

大根堆示例

image-20230121212945071

小根堆示例

1.2 堆的性质

  1. 堆是一颗完全二叉树;
  2. 堆中某结点的值总是不大于或不小于其父节点的值;

二、堆的实现

2.1 算法构思

  堆的实现可以使用数组亦可以使用链表,链表的复杂度较高,相比较则数组较低;数组可以利用下标来映射各结点之间的父子或兄弟等关系;此外,数组较链表在尾插上更优。

  下面以数组array[ ]={27,15,19,18,28,34,65,49,25,37}为例子,进行算法相关接口思路的构思。

2.1.1 堆的插入

堆的插入接口在构造的时候须注意:

  1. 构建堆的数组的存储空间是否满负荷,若满了则须扩容,并在数组未部插入新的数据;
  2. 下图为数组插入的示图,此时的二叉树(数组)并不是堆,需要向上调整;

image-20230121221859153

数组构建二叉树

   3.若二叉树不是堆,则需要向上调增(默认大根堆,向上调整详见2.1.2节);

2.1.2 向上调整

  通过观察数组存储结构与逻辑关系,可以得出父结点与子结点的相关函数关系:
c h i l d L e f t = p a r e n t ∗ 2 + 1 c h i l d R i g h t = p a r e n t ∗ 2 + 2 p a r e n t = ( c h i l d − 1 ) / 2 childLeft=parent*2+1\\ childRight=parent*2+2\\ parent=(child-1)/2 childLeft=parent2+1childRight=parent2+2parent=(child1)/2
上式中,childparent为数组的下标位置。向上调整具体步骤为:

  1. 在构建的堆中,插入整数90,则破坏的堆,此时的插入数90比其父结点大,需要向上调整;
  2. 在被影响的子树中,插入的数直接为child,寻找其父结点parent,若子结点比父结点处的值大,则将父子结点处的数值交换;
  3. 继续上步操作,比较父子结点的大小,知道child>0结束。

相关步骤图示详见下图:

image-20230121224313815

向上调整

2.1.3 堆的删除

  在实际工程应用中,需要删除堆顶,但在本文中为了降低算法复杂度,采用了数组来构造堆,而数组删除首元素需要进行数组迁移。为了降低空间复杂度,我们将数组的首尾元素进行交换,再删除数组的尾部元素,从而间接删除首元素。

  此时,需要将新的首元素进行向下调整来维持堆,具体的操作详见下一小节(2.1.4)。

2.1.4 向下调整

  首先将堆中的根90与叶子37进行交换,删除交换到数组尾步的90;接下来通过向下调整来维持大根堆,向下调整具体步骤:

  1. 若子结点有两个,则选择子结点中较大的作为child,与parent进行比较;若parentchild小,则进行数值交换,并将parent放置于child的位置;
  2. 重新计算child,继续上述步骤;
  3. child的值等于或超过数组元素的数量时,向下调整结束。

相关步骤图示详见下图:

image-20230121231800394

向下调整

2.2 堆的代码实现

2.2.1 堆的初始化

void HeapInit(HP* php)
{
    assert(php);

    php->a=NULL;
    php->capacity=php->size=0;

}

2.2.2 堆的打印

void HeapPrint(HP* php)
{
    assert(php);
    
    for(int i = 0;i < php->size;i++)
    {
        printf("%d ",php->a[i]);
    }
    printf("\n");
}

2.2.3 堆的销毁

void HeapDestroy(HP* php)
{
    assert(php);

    free(php->a);
    php->a=NULL;
    php->size=php->capacity=0;
}

2.2.4 堆的插入

void HeapPush(HP* php,HPDataType x)
{
    assert(php);

    if(php->size==php->capacity)
    {
        int newCapacity=php->capacity==0?4:php->capacity*2;
        HPDataType* tmp=(HPDataType*)realloc(php->a,sizeof(HPDataType)*newCapacity);
        if(tmp == NULL)
        {
            perror("realloc fail");
            exit(-1);
        }
        php->a=tmp;
        php->capacity=newCapacity;

    }
    php->a[php->size]=x;
    php->size++;
    AdjustUP(php->a,php->size-1);
}

2.2.5 向上调整接口

void AdjustUP(HPDataType* a,int child)
{
    int parent = (child-1)/2;
    while(child>0)
    {
        if(a[child]>a[parent])
        {
            Swap(&a[child],&a[parent]);
            child=parent;
            parent=(child-1)/2;
        }
        else
        {
            break;
        }
    }
}

数值交换函数

void Swap(HPDataType* p1,HPDataType* p2)
{
    HPDataType tmp=*p1;
    *p1=*p2;
    *p2=tmp;
}

2.2.6 堆的元素删除

void HeapPop(HP* php)
{
    assert(php);
    assert(php->size>0);

    Swap(&php->a[0],&php->a[php->size-1]);
    php->size--;
    AdjustDown(php->a,php->size,0);

}

2.2.7 向下调整接口

void AdjustDown(HPDataType* a,int n,int parent)
 {
    int child = parent*2+1;
    while(child<n)
    {
        if((child+1)<n && a[child]<a[child+1])
        {
            ++child;
        }
        if(a[parent]<a[child])
        {
            Swap(&a[parent],&a[child]);
            parent=child;
            child=parent*2+1;
        }
        else
        {
            break;
        }
    }
 }

三、测试

3.1 通过数组array构造堆

void TestHeap()
{
    int array[]={27,15,19,18,28,34,65,49,25,37};
    HP hp;
    HeapInit(&hp);
    for(int i=0;i<sizeof(array)/sizeof(int);i++)
    {
        HeapPush(&hp,array[i]);
    }
    HeapPrint(&hp);
    HeapDestroy(&hp);
}

测试结果:

image-20230121233039192

结果逻辑化,下图所示;本文算法实现了输入数组的建堆功能。

image-20230121233243819

3.2 取堆顶来获取数组的最大数

void TestHeap2()
{
    int array[]={27,15,19,18,28,34,65,49,25,37};
    HP hp;
    HeapInit(&hp);
    for(int i=0;i<sizeof(array)/sizeof(int);i++)
    {
        HeapPush(&hp,array[i]);
    }
    int k=5;
    while(k--)
    {
        printf("%d ",HeapTop(&hp));
        HeapPop(&hp);
    }
    
    HeapDestroy(&hp);
}

测试结果:

image-20230121233928038

连续5次取堆顶删除,获取数组的前5个最大的数。

祝各位春节快乐,阖家欢乐!
在这里插入图片描述

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

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

相关文章

【14】C语言_函数简介

目录 1、C语言中函数的分类: 2、库函数 3、自定义函数 1、C语言中函数的分类: 1.库函数 2.自定义函数 2、库函数 为什么会有库函数? 1.我们知道在我们学习C语言编程的时候&#xff0c;总是在一个代码编写完成之后迫不及待的想知道结果&#xff0c;想把这个结果打印到我们的屏…

ESP32设备驱动-LX1972可见光传感器驱动

LX1972可见光传感器驱动 1、LX1972介绍 LX1972 是一款低成本硅光传感器,其光谱响应非常接近人眼。专利电路在 520nm 处产生峰值光谱响应,IR 响应小于峰值响应的 5%,高于 900nm。 光电传感器是一个 PIN 二极管阵列,具有线性、准确和非常可重复的电流传递函数。 芯片上的…

扫盲-从零开始搭建阿里云流媒体服务器/音视频编解码/

1.基础概念 2.简单模式-HTTP文件服务器 存储采用NAS&#xff0c;服务器配置 采用FASTDFS (192条消息) Linux新手入门系列&#xff1a;FastDFS单机部署一键安装脚本_IT小胖豆的博客-CSDN博客 有几个坑&#xff1a; 常用命令&#xff1a; tail -20f /usr/local/nginx/logs/er…

3小时精通opencv(三)图片裁剪与形状绘制

3小时精通opencv(三)图片裁剪与形状绘制 参考视频资源:3h精通Opencv-Python 文章目录3小时精通opencv(三)图片裁剪与形状绘制图片裁剪绘制形状绘制直线绘制矩形绘制圆形绘制文字整体代码图片裁剪 图片裁剪不需要使用opencv中特有的函数, 对于opencv中读取到的图像, 直接当做矩…

15. python数据类型转换

1. 隐式类型转换 - 自动完成 在隐式类型转换中&#xff0c;Python 会自动将一种数据类型转换为另一种数据类型&#xff0c;不需要我们去干预。 (1) 以下实例中&#xff0c;我们对两种不同类型的数据进行运算&#xff0c;较低数据类型&#xff08;整数&#xff09;就会转换为较…

java 探花交友项目day4 MongoDB

数据库表 接口定义 其他都比较简单 我们讲黑名单查询页面的设计 DubboService public class BlackListApiImpl extends ServiceImpl<BlackListMapper,BlackList> implements BlackListApi{Autowiredprivate BlackListMapper blackListMapper;Autowiredprivate UserInf…

Allegro如何输出坐标文件操作指导

Allegro如何输出坐标文件操作指导 PCB在SMT的时候会需要用坐标文件,Allegro支持输出坐标文件,如下图 具体操作如下 选择Tools选择report出现repor

LINUX学习之正则表达式(十二)

普通正则 元字符 元字符匹配描述.匹配除了换行符以外的任意单个字符*前导字符出现0次或连续多次.*任意长度字符^行首(以…开头)$行尾(以…结尾)^$空行[]匹配括号里任意单个字符或一组单个字符[^]匹配不包含括号里任一单个字符或一组单个字符^[]匹配以括号里任意单个字符或一组…

Python技能树-推导式

Python 列表推导式(1) Python 独步天下的推导式表达式&#xff0c;使用列表推导式过滤出偶数列表 # -*- coding: UTF-8 -*- if __name__ __main__:list [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]print()print("# 使用列表推导式过滤出偶数")# TODO(you): 请在此实现过滤代…

Allegro如何添加平衡铜操作指导

Allegro如何添加平衡铜操作指导 PCB在加工的时候,工厂会添加平衡铜,Allegro支持自动加上平衡铜,如下图 具体操作如下 选择Manufacture点击Thieving

比较器: Comparable 与 Comparator 区别

比较器&#xff1a; Comparable 与 Comparator 区别 每博一文案 师父说: 人不能精得过火&#xff0c;太精明的人往往让人生厌&#xff0c;人也别傻的可怜&#xff0c;一腔热血付出却白忙一场。 太精明的人&#xff0c;凡事都想要争个明明白白&#xff0c;每一分钱都要和人计较…

macOS Ventura 13.1 系统问题:掉电快 充电慢

今年一月份升级了 MBA 的系统&#xff0c;之后的笔记本&#xff1a; 使用过程&#xff1a;电量不禁用&#xff0c;掉电很快。 充电过程&#xff1a;很慢。而且存在一定几率&#xff1a;电量充到某个值&#xff08;如30%&#xff09;之后不管再充多久还是这个电量值。 系统信息…

HDFS高可用单NameNode从standby恢复为active(二)

1、背景 有一个hdfs高可用集群&#xff0c;因为某些操作&#xff0c;导致其中一个namenode的信息全部丢失了。最后只剩下一个完整的namenode信息和datanode信息。于是在在启动hdfs后发现独有的namenode始终处于standby状态。即使通过hdfs haadmin -transitionToActive命令也不能…

java面向接口编程2023027

那就再进一步&#xff1a;面向接口编程 面向接口编程前面已经提到&#xff0c;接口体现的是一种规范和实现分离的设计哲学&#xff0c;充分利用接口可以极好地降低程序各模块之间的耦合&#xff0c;从而提高系统的可扩展性和可维护性。 基于这种原则&#xff0c;很多软件架构设…

Leetcode——第 329 场周赛

题目链接&#xff1a; 交替数字和 根据第 K 场考试的分数排序 执行逐位运算使字符串相等 拆分数组的最小代价 题目描述 交替数字和 给你一个正整数 n 。n中的每一位数字都会按下述规则分配一个符号&#xff1a; 最高有效位 上的数字分配到 正 号。 剩余每位上数字的符号都与…

学习记录666@项目管理之项目质量管理

什么项目质量管理 所谓项目质量&#xff0c;是项目满足需求的程度&#xff1b;所谓质量管理&#xff0c;就是为了使项目满足需求的程度足够高或者是合适程度下而进行的一系列管理活动。 包括规划质量管理、实施质量保证、质量控制三个方面。 规划质量管理 规划质量管理主要…

JUC面试(七)——CountDownLatchCyclicBarrierSemaphore

CountDownLatch 概念 让一些线程阻塞直到另一些线程完成一系列操作才被唤醒 CountDownLatch主要有两个方法&#xff0c;当一个或多个线程调用await方法时&#xff0c;调用线程就会被阻塞。其它线程调用CountDown方法会将计数器减1&#xff08;调用CountDown方法的线程不会被…

nacos源码分析==客户端从服务端读取配置文件-服务端服务注册

客户端从服务端读取配置文件 客户端启动的时候会扫描到boostrap.yml中的信息&#xff0c;扫描到标签ConditionalOnProperty会将NacosConfigBootstrapConfiguration 中的bean注入。其中NacosConfigProperties就是读取的boostrap.yml中spring.cloud.nacos.config下的配置项。Nac…

“深度学习”学习日记。与学习有关的技巧--权重的初始值

2023.1.22 在深度学习的学习中&#xff0c;权重的初始值特别重要。这关系到神经网络的学习能否成功&#xff1b; 在以前误差反向传播法和神经网络学习的算法实现这两篇文章中&#xff0c;对权重的初始值的确定是这样的&#xff1a; class TwoLayerNet:def __init__(self, inp…

在2022年的最后一天我学会了哈希表

文章目录前言STL相关容器unordered_setunordered_map哈希表哈希冲突闭散列开散列STL相关容器的模拟实现用一个哈希表改造两个容器哈希表的迭代器总结前言 首先先提前祝贺大家新年快乐&#xff01;本文是农历2022年的最后一篇博客。而今天我们介绍的也是STL里面重要的一个数据结…