利用C语言模拟实现堆的基本操作和调堆算法

利用C语言模拟实现的基本操作和调算法


文章目录

  • 利用C语言模拟实现的基本操作和调算法
    • 前言
    • 一、的基本原理
      • 大根和小根的比较
    • 二、实现的基本操作
      • 1)结构定义
      • 2)初始化(HeapInit)
      • 3)销毁(HeapDestroy)
      • 4)的调整算法
      • 5)的插入操作(HeapPush)
      • 6)的删除操作(HeapPop)
      • 7)获取顶元素(HeapTop)
      • 8)获取的大小(HeapSize)
      • 9)判断是否为空(HeapEmpty)
      • 10)打印(HeapPrint)
    • 三、测试的功能
    • 总结

前言

是一种重要而高效的数据结构,广泛应用于各种算法和数据处理场景。本篇博客将介绍如何使用C语言模拟实现的基本操作,包括初始化、插入、删除等,并通过回调函数和函数指针的灵活运用,实现大根和小根的不同调算法


一、的基本原理

是一种特殊的树形数据结构,具有以下基本特点:

  • 完全二叉树的结构,即除了最底层,其他层都是满的。
  • 中的每个节点的值都必须大于等于或小于等于其子节点的值,根据此条件分为大根和小根

常被用来实现优先队列等数据结构,其中最值的快速访问是的主要优势

大根和小根的比较

​ 大根和小根的不同之处在于调算法中的比较条件。在大根中,父节点的值必须大于子节点的值;而在小根中,父节点的值必须小于子节点的值。通过函数指针,我们可以根据用户的选择动态切换调算法,使代码更加灵活。

二、实现的基本操作

声明: 此处采用动态数组模拟实现完全二叉树

1)结构定义

定义了的结构,包括元素数组、大小、容量和标识是大根还是小根的字符。

typedef int HPDataType;

typedef struct Heap {
    HPDataType* a;
    size_t size;
    size_t capacity;
    char RootBigOrSmall;
} HP;

2)初始化(HeapInit)

通过 HeapInit 函数初始化结构,用户可以选择大根(‘B’或’b’)或小根(‘S’或’s’)。

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

    printf("请挑选大根(big -> B)或小根(small -> S): 【输入首字母大小写】");
    char choose = ' ';
    choose = getchar();
    if (choose == 'B' || choose == 'S' || choose == 'b' || choose == 's') { php->RootBigOrSmall = choose; }
    else { printf("输入有误!"); return; }

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

3)销毁(HeapDestroy)

释放内存和相关资源的 HeapDestroy 函数。

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

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

4)的调整算法

(1)向上调算法
void adjustUpHeap(HPDataType* a, size_t child, bool (*cmp)(HPDataType _parent, HPDataType _child))
{
    size_t parent = (child - 1) / 2;
    if (child == 0) { parent = 0; }

    while (child > 0)
    {
        if (cmp(a[parent], a[child]))
        {
            HPDataType tmp = a[parent];
            a[parent] = a[child];
            a[child] = tmp;
        }

        child = parent;
        parent = (parent - 1) / 2;
    }
}
(2)向下调算法
void adjustDownHeap(HPDataType* a, HPDataType size, HPDataType parent,
    bool (*cmp)(HPDataType _parent, HPDataType _child))
{
    HPDataType child = parent * 2 + 1;

    while (child < size)
    {
        if (child + 1 < size && cmp(a[child], a[child + 1]))
        {
            child++;
        }

        if (cmp(a[parent], a[child]))
        {
            HPDataType tmp = a[parent];
            a[parent] = a[child];
            a[child] = tmp;
            parent = child;
            child = child * 2 + 1;
        }
        else { break; }
    }
}

这里采用函数指针作为两种调算法的参数是因为通过此方法可实现运行后指定大根或小根从而影响相关控制的操作,比如 HeapPushHeapPop等操作。

5)的插入操作(HeapPush)

通过 HeapPush 函数插入元素,并通过向上调算法保持的性质。

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

    // 检查扩容
    if (php->capacity == php->size)
    {
        size_t new_capacity = (php->capacity == 0 ? 4 : php->capacity * 2);
        HPDataType* tmp = (HPDataType*)realloc(php->a, new_capacity * sizeof(HPDataType));
        if (!tmp) { perror("realloc of HeapPush"); return; }

        php->a = tmp;
        php->capacity = new_capacity;
    }

    php->a[php->size++] = x;

    // 调
    bool (*cmp)(HPDataType _parent, HPDataType _child) = NULL;
    if (php->RootBigOrSmall == 'B' || php->RootBigOrSmall == 'b')
    {
        cmp = bigRootHeap_cmp;
    }
    else
    {
        cmp = smallRootHeap_cmp;
    }
    adjustUpHeap(php->a, php->size - 1, cmp);
}

注意: 这里使用动态数组存储的节点,所以需要考虑空间扩容问题,当容量不足时利用 realloc() 函数从区申请额外空间。

6)的删除操作(HeapPop)

通过 HeapPop 函数删除顶元素,并通过调算法保持的性质。

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

    bool (*cmp)(HPDataType _parent, HPDataType _child) = NULL;
    if (php->RootBigOrSmall == 'B' || php->RootBigOrSmall == 'b')
    {
        cmp = bigRootHeap_cmp;
    }
    else
    {
        cmp = smallRootHeap_cmp;
    }

    // 去顶节点
    HPDataType tmp = php->a[0];
    php->a[0] = php->a[php->size - 1];
    php->a[php->size - 1] = tmp;
    php->size--;

    // 调
    adjustDownHeap(php->a, php->size, 0, cmp);
}

7)获取顶元素(HeapTop)

通过 HeapTop 函数获取顶元素。

HPDataType HeapTop(const HP* php)
{
    assert(php);
    assert(php->a && php->size > 0);

    return php->a[0];
}

8)获取的大小(HeapSize)

通过 HeapSize 函数获取的大小。

size_t HeapSize(const HP* php)
{
    assert(php);

    return php->size;
}

9)判断是否为空(HeapEmpty)

通过 HeapEmpty 函数判断是否为空。

bool HeapEmpty(const HP* php)
{
    assert(php);

    return php->size == 0;
}

10)打印(HeapPrint)

通过 HeapPrint 函数输出的内容。(测试时避免代码冗杂)

void HeapPrint(const HP* php)
{
    assert(php);

    for (int i = 0; i < php->size; i++)
    {
        std::cout << php->a[i] << '\t';
    }
    std::cout << "\n";
}

三、测试的功能

测试代码:

int main()
{
	HP hp;
	HeapInit(&hp);
	HeapPush(&hp, 15);
	HeapPush(&hp, 18);
	HeapPush(&hp, 19);
	HeapPush(&hp, 25);
	HeapPush(&hp, 28);
	HeapPush(&hp, 34);
	HeapPush(&hp, 65);
	HeapPush(&hp, 49);
	HeapPush(&hp, 27);
	HeapPush(&hp, 37);

	HeapPrint(&hp);

	HeapPush(&hp, 30);
	HeapPrint(&hp);

	HeapPop(&hp);
	HeapPrint(&hp);

	HeapDestroy(&hp);

	return 0;
}

通过 HeapPrint可以将上面代码分为三部分,第一次 HeapPrint时,对应数组内元素为:

在这里插入图片描述

树状图:

在这里插入图片描述

第二次HeapPrint时:

经历了HeapPush功能,对应数组内元素为:

在这里插入图片描述

树状图:

在这里插入图片描述

第三次HeapPrint时:

经历HeapPop功能:(向下调整算法

在这里插入图片描述

所得对应数组内元素为:

在这里插入图片描述

运行结果:

在这里插入图片描述

无内存泄漏,HeapDestroy符合设计需求。


总结

​ 本文详细介绍了使用C语言模拟实现的基本操作和调算法。通过回调函数和函数指针,实现了大根和小根的灵活切换。是一种强大的数据结构,能够在很多应用中提供高效的解决方案。在实际编程中,理解和掌握的实现原理对于优化算法和提高代码效率至关重要。

在这里插入图片描述


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

相关文章

Linux内存管理 | 六、物理内存分配——伙伴系统

我的圈子&#xff1a; 高级工程师聚集地 我是董哥&#xff0c;高级嵌入式软件开发工程师&#xff0c;从事嵌入式Linux驱动开发和系统开发&#xff0c;曾就职于世界500强企业&#xff01; 创作理念&#xff1a;专注分享高质量嵌入式文章&#xff0c;让大家读有所得&#xff01; …

力扣 145. 二叉树的后序遍历

目录 1.解题思路2.代码实现 1.解题思路 与前序&#xff0c;中序相同,将树的值存到数组中&#xff0c;所以在申请空间的时候&#xff0c;我们需要知道要申请多少空间&#xff0c;也就是要知道树到底有多少个结点&#xff0c;因此第一步要写个函数实现获得树的节点数&#xff0c…

7.构建简单企业网络

7.构建简单企业网络 也可以在接入层上面加台汇聚层交换机,组成三层网络架构 交换机根据MAC地址转发报文 交换机工作在数据链路层 例如&#xff1a;PCA要访问PCB&#xff0c;报文就从2口发出,因为交换机里有个映射表, 2口所连接设备的MAC地址交换机是知道的 所以PCA的报文到…

CTF竞赛密码学题目解析

CTF&#xff08;Capture The Flag&#xff09;竞赛是一个有趣的挑战。密码学是CTF竞赛中的核心元素之一&#xff0c;通常涉及解密、破译密码、理解加密算法等技能。以下是30个题目及答案&#xff0c;新入行的可以看看鸭。 题目及答案 1. Caesar Cipher 描述&#xff1a;给出一…

1、springboot项目运行

问题1&#xff1a;获取不到配置文件的参数 我的配置文件获取的参数如下&#xff1a; public class Configures{Value("${configmdm.apk.apkName}")private static String apkName;private void setApkName(String apkName) {Configures.apkName apkName;}private …

定时器TIM HAL库+cubeMX(上)

定时器时钟源APB1 36MHz 一.基本定时器 1.基本框图 2.溢出时间计算 3.配置定时器步骤 TIM_HandleTypeDef g_timx_handle;/* 定时器中断初始化函数 */ void btim_timx_int_init(uint16_t arr, uint16_t psc) {g_timx_handle.Instance TIM6;g_timx_handle.Init.Prescaler p…

1.6 实战:Postman请求Get接口-获取用于登录的图形验证码

上一小节我们学习了Postman的布局,对Postman有了一个整体的认知,本小节我们就来实操一下Get接口。 我们打开Postman,点击我们之前创建的请求”获取登录页验证码“。我们在地址栏里填入获取登录页验证码的接口地址。怎么查看这个接口地址呢?我们打开校园二手交易系统,打开…

【论文阅读】Video-to-Video Synthesis

基于条件GAN的视频到视频生成。 introduce 总结&#xff1a;基于条件GAN的视频到视频生成。将视频到视频的合成问题视为一个分布匹配问题。 动机&#xff1a;GAN生成的视频很难保证前后帧的一致性&#xff0c;容易出现抖动。本文加入前后帧的光流信息作为约束。Vid2Vid作为pix…