【数据结构】二叉树之堆的实现

news/2024/5/20 0:27:00 标签: 数据结构, 算法, c语言, c++, 推荐算法

🔥博客主页:小王又困了

📚系列专栏:数据结构

🌟人之为学,不日近则日退

❤️感谢大家点赞👍收藏⭐评论✍️


目录

一、二叉树的顺序结构

📒1.1顺序存储

📒1.2堆的性质

📒1.3堆的分类

二、堆的实现

📒2.1堆的创建

📒2.2堆的初始化

📒2.3堆的插入

📒2.4向上调整数据

📒2.5堆的删除

📒2.6向下调整数据

📒2.7堆的销毁


🗒️前言:

在上一期的文章中我们学习了一些二叉树的知识,也了解了堆的概念。堆是一颗完全二叉树,分为大堆和小堆,今天我们将实现堆的各种功能。

一、二叉树的顺序结构

📒1.1顺序存储

顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费。而现实使用中只有堆才会使用数组来存储。二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。

📒1.2堆的性质

  • 堆中某个节点的之总是不大于或不小于其父亲节点的值
  • 堆是一颗完全二叉树

📒1.3堆的分类

  • 小堆:树中任意一个父亲都小于等于孩子
  • 大堆:树中任意一个父亲都大于等于孩子

二、堆的实现

📒2.1堆的创建

堆的逻辑结构是树形结构,是我们想象出来的,实际上我们操作的数组,所以堆的创建和顺序表的结构相同。

typedef int HPDateType;
typedef struct Heap
{
    HPDateType* a;
    int size;
    int capacity;
}HP;

📒2.2堆的初始化

我们有两种初始化的方式,一种是在初始化阶段不开辟空间,在插入过程中进行扩容;另一种是在初始化阶段就开辟空间。

void HeapInit(HP* php)
{
    assert(php);
    php->a = NULL;
    php->size = 0;
    php->capacity = 0;
}
void HeapInit(HP* php)
{
    assert(php); 
    php->size = 0;
    php->capacity = 5;
    php->a = (HPDateType*)malloc(sizeof(HPDateType) * capacity);
    if (tmp == NULL)
    {
        perror("malloc");
        exit(-1);
    }
}

📒2.3堆的插入

堆是使用顺序结构的数组来存储的,我们使用尾插插入数据更方便,然后将数据调整到合适的位置。

4143c6b8ae344af89a1c07f578efa8b6.jpeg

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);
}

如图:在小堆中插入50,50比它的父亲小,所以要交换两数的位置。我们知道孩子的下标通过 parent=(child-1)/2 就可以得到父亲的下标,然后交换两数。

📒2.4向上调整数据

如果是小堆存储我们通过孩子的下标找到父亲,比较两数如果孩子小于父亲就交换,然后在向上比较,如果孩子不小于父亲就跳出循环。

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

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 = (parent - 1) / 2;
        }
        else
        {
            break;
        }
    }
}

📒2.5堆的删除

我们使用挪动覆盖的方法删除根,会使关系混乱,剩下的值不一定是堆,而且效率很低。这里提供一种更好的方法,将根和最后一个值交换,然后删除,最后调整数据。

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);
}

这里要注意有数据的时候才能删除,所以要加入 assert(php->size > 0) 进行判断。

📒2.6向下调整数据

如果是小堆存储我们要找到左右孩子中较小的数,然后与父亲交换,再找到下一层重复步骤,直到找到叶节点结束。

void AdjustDown(HPDataType* a, int n, int parent)
{
    //默认左孩子是较小的
    int child = parent * 2 + 1;
    while (child < n)
    {
        // 找出小的那个孩子
        if (child + 1 < n && a[child + 1] < a[child])
        {
            ++child;
        }

        if (a[child] < a[parent])
        {
            Swap(&a[child], &a[parent]);
            // 继续往下调整
            parent = child;
            child = parent * 2 + 1;
        }
        else
        {
            break;
        }
    }
}

📒2.7堆的销毁

我们使用动态开辟内存,要及时释放空间并置为空指针,不然会造成数据泄露。

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

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

本次的内容到这里就结束啦。希望大家阅读完可以有所收获,同时也感谢各位读者三连支持。文章有问题可以在评论区留言,博主一定认真认真修改,以后写出更好的文章。你们的支持就是博主最大的动力。


http://www.niftyadmin.cn/n/5044351.html

相关文章

Blender入门——快捷键

视角控制 旋转视角&#xff1a;鼠标中键摁住即可旋转平移视角&#xff1a;shift中远近视角&#xff1a;中键滚动 物体控制 移动物体&#xff1a;G旋转物体:R缩放物体&#xff1a;S回复变换&#xff1a;AltG,R,S新建物体&#xff1a;shiftA复制物体&#xff1a;shiftD删除物体&a…

【go.mod file not found in current directory or any parent directory】

go编译程序出现错误提示&#xff1a; go.mod file not found in current directory or any parent directory; see ‘go help modules’ (exit status 1) 解决方法&#xff1a; cmd切换到go工程目录下&#xff0c;执行: go mod init 工程名称

1400*C. No Prime Differences(找规律数学)

解析&#xff1a; 由于 1 不是质数&#xff0c;所以我们令每一行的数都相差 1 对于行间&#xff0c;分为 n、m之中有存在偶数和都为奇数两种情况。 如果n、m存在偶数&#xff0c;假设m为偶数。 如果都为奇数&#xff0c;则&#xff1a; #include<bits/stdc.h> using name…

连接器公司介绍

连接器是模块之间的桥梁&#xff0c;在选型中考虑的因素较多。特别是在信号完整性领域&#xff0c;涉及到高速、射频方向需要全方位评估。记录几家全球连接器知名企业方便后续访问学习。 泰科连接器第一名 泰科电子TEConnectivity&#xff08;简称“TE”&#xff09;总部位于瑞…

C++类模板学习

之前已经学习了函数模板&#xff0c;在这里&#xff0c; C函数模板Demo - win32 版_c编写的opc da demo_bcbobo21cn的博客-CSDN博客 下面学习类模板&#xff1b; VC6&#xff1b; 做一个星星类&#xff0c;Star&#xff1b; Star.h&#xff1b; #if !defined(AFX_STAR_H_…

批量更改文件名 - 大师汇总

在PC看到了下载的电影&#xff0c;其中名称包含了各大网络高手贡献&#xff0c;名字长很有纪念意义。 但对于播放者而言&#xff0c;无法根据文件名短时间内判断出电影名字。汗一个&#xff01; 这几天&#xff0c;大师收集更改文件名的资料并实践一把&#xff0c;列在下面。…

从统计语言模型到预训练语言模型---预训练语言模型(BERT,GPT,BART系列)

基于 Transformer 架构以及 Attention 机制&#xff0c;一系列预训练语言模型被不断提出。 ​BERT 2018 年 10 月&#xff0c; Google AI 研究院的 Jacob Devlin 等人提出了 BERT (Bidirectional Encoder Representation from Transformers ) 。具体的研究论文发布在 arXiv …

在Bat To Exe Converter,修改为当异常结束或终止时,程序重新启动执行

在Bat To Exe Converter&#xff0c;修改为当异常结束或终止时&#xff0c;程序重新启动执行 .bat中的代码部分&#xff1a; .bat中的代码echo offpython E:\python\yoloProjectTestSmallLarge\detect.pypause&#xff0c;我想你能帮在Bat To Exe Converter&#xff0c;修改成…