大小堆的实现

发布时间:2023年12月26日

目录

?0.引言

1.数据类型的定义

2.堆的初始化函数

3.堆的销毁函数

4.堆的打印函数

5.堆的交换函数

6.堆的向上调整函数

7.堆的插入函数

8.堆的向下调整函数

9.堆的删除函数

10.取堆顶的元素

11.堆的数据个数

12。堆的判空


?0.引言

这段代码主要定义了一个堆的数据结构和相关操作。堆是一种特殊的树形数据结构,它满足堆的性质要求,通常被用于实现优先队列等数据结构。堆可以作为最大堆或最小堆来使用,这取决于如何对数据进行初始化。

主要函数包括: * `HeapInit(Heap* hp)`:初始化堆的数据结构,包括设置堆的元素指针数组、当前元素个数、最大容量为NULL和0。

* `HeapDestroy(Heap* hp)`:销毁堆的数据结构,释放堆中的内存空间并设置为NULL。 * `HeapPrint(Heap* hp)`:打印堆中的元素并换行。

* `Swap(HPDataType* _a, int child, int parent)`:交换两个元素的值。

* `AdjustUp(HPDataType* _a, int child)`:向上调整函数,调整堆的性质以保证满足堆的性质要求(最大堆或最小堆)。

* `HeapPush(Heap* hp, HPDataType x)`:插入新的元素到堆中,并调整堆的性质以满足堆的要求。

* `AdjustDown(Heap* hp)`:向下调整函数,调整堆的性质以满足堆的性质要求(最大堆或最小堆)。

* `HeapPop(Heap* hp)`:删除堆顶元素并调整堆的性质以满足堆的要求。

* `HeapTop(Heap* hp)`:返回堆中的第一个元素的值。

* `HeapSize(Heap* hp)`:返回堆中的元素个数。

* `HeapEmpty(Heap* hp)`:判断堆是否为空。

这段代码还包含了一些断言(assert)语句,用于在代码执行过程中检查某些条件是否为真,如果条件不满足,程序会抛出异常并退出。这对于调试代码非常有用,可以帮助开发者快速定位问题所在。


1.数据类型的定义

// 头文件包含
#include<stdio.h> // 输入输出库,用于输入输出操作
#include<stdlib.h> // 常用的一些函数库,例如malloc, realloc等
#include<assert.h> // 断言库,用于调试代码
#include<stdbool.h> // 定义bool类型和相关的函数

// 定义堆的数据类型和结构体
typedef int HPDataType; // 将int类型命名为HPDataType,用于表示堆中的数据类型
typedef struct Heap // 定义一个结构体Heap,用于表示堆的数据结构
{
    HPDataType* _a; // 指向堆中元素的指针数组
    int _size; // 当前堆中元素的个数
    int _capacity; // 堆的最大容量
}Heap; // 结构体名称为Heap


2.堆的初始化函数

// 堆的初始化函数,将堆的元素指针数组、当前元素个数、最大容量都初始化为NULL和0
void HeapInit(Heap* hp)
{
    hp->_a = NULL;
    hp->_capacity = 0;
    hp->_size = 0;
}


3.堆的销毁函数

// 堆的销毁函数,释放堆中的内存空间,并将元素指针数组、当前元素个数、最大容量都设置为NULL和0
void HeapDestory(Heap* hp)
{
    assert(hp); // 断言检查堆是否为空,如果为空则调用free释放内存并设置指针为NULL
    free(hp->_a); // 释放内存空间
    hp->_a = NULL; // 将指针设置为NULL
    hp->_capacity = 0; // 将最大容量设置为0
    hp->_size = 0; // 将当前元素个数设置为0
}


4.堆的打印函数

// 堆的打印函数,输出堆中的元素并换行
void HeapPrint(Heap* hp)
{
    assert(hp); // 断言检查堆是否为空,如果为空则直接返回
    int i = 0; // 循环变量,遍历所有元素并输出
    for (i = 0; i < hp->_size; i++) // 遍历所有元素并输出到控制台
    {
        printf("%3d", hp->_a[i]); // 使用printf输出元素的值,使用%3d控制输出宽度为3个字符并左对齐
    }
    printf("\n"); // 换行符,输出完所有元素后换行
}


5.堆的交换函数

// 交换函数,交换两个元素的值
void Swap(HPDataType* _a, int child, int parent)
{
    assert(_a); // 断言检查_a是否为空指针,如果为空则直接返回或抛出异常
    int tmp = 0; // 临时变量用于交换两个元素的值
    tmp = _a[child]; // 将child位置的元素值赋给临时变量tmp
    _a[child] = _a[parent]; // 将parent位置的元素值赋给child位置的原值(相当于将parent位置的值交换到了child位置)
    _a[parent] = tmp; // 将临时变量tmp的值赋给parent位置的新值(相当于将原来的child位置的值换为了parent位置的新值)
}


6.堆的向上调整函数

// 向上调整函数,调整堆的性质以保证满足堆的性质要求(最大堆或最小堆)
void AdjustUp(HPDataType* _a, int child) // child为需要调整的元素的索引位置(下标从0开始)
{
    assert(_a); // 断言检查_a是否为空指针,如果为空则直接返回或抛出异常
    int parent = 0; // 上一个父节点索引位置(下标从0开始)
    while (child > 0) // 当child大于等于0时循环执行以下操作(即当前节点不是根节点)
    {
        parent = (child - 1) / 2; // 根据二叉树的上序遍历规则计算出上一个父节点的索引位置(注意是除以2而不是减1)
        if (_a[parent] > _a[child]) // 如果上一个父节点的值大于当前节点的值(说明不符合堆的性质要求)
        {
            Swap(_a, child, parent); // 则交换当前节点和上一个父节点的值(即调整当前节点的值到符合要求的位置)并继续向下调整或者循环结束(即完成了一次向上调整)
            child = parent; // 将当前节点设为需要调整的节点,重新开始向上遍历下一层节点
        }
        else
        {
            break;
        }
    }
}


7.堆的插入函数

// 堆的插入函数,将新的元素插入到堆中
void HeapPush(Heap* hp, HPDataType x)
{
    assert(hp); // 断言检查hp是否为空指针,如果为空则直接返回或抛出异常
    if (hp->_capacity == hp->_size) // 如果堆的当前元素个数等于最大容量(堆已满)
    {
        int newcapacity = hp->_capacity == 0 ? 4 : hp->_capacity * 2; // 将新的容量设置为原最大容量的两倍(如果原最大容量为0,则设置为4)
        HPDataType* newheap = (HPDataType*)realloc(hp->_a, sizeof(HPDataType) * newcapacity); // 使用realloc函数重新分配内存空间,将堆的元素指针数组大小调整为新的容量大小
        if (newheap == NULL) // 检查内存分配是否成功
        {
            perror("realloc"); // 输出错误信息
            exit(-1); // 程序异常退出
        }
        hp->_a = newheap; // 将新分配的内存空间赋给堆的元素指针数组
        hp->_capacity = newcapacity; // 将堆的最大容量更新为新的容量大小
    }
    hp->_a[hp->_size] = x; // 在堆的末尾插入新的元素
    AdjustUp(hp->_a, hp->_size); // 调整堆的性质以满足堆的要求
    hp->_size++; // 堆的元素个数自增
}

?


8.堆的向下调整函数

// 向下调整函数,调整堆的性质以保证满足堆的性质要求(最大堆或最小堆)
void AdjustDown(Heap* hp)
{
    int parent = 0; // 当前父节点的索引位置(下标从0开始)
    int child = 0; // 当前子节点的索引位置
    while (parent * 2 + 1 < hp->_size) // 当父节点的左子节点存在时循环执行以下操作
    {
        child = parent * 2 + 1; // 根据二叉树的左子节点索引位置计算出子节点的索引位置
        if (child + 1 < hp->_size && hp->_a[child] > hp->_a[child + 1]) // 如果子节点的右子节点存在且右子节点的值小于左子节点的值
        {
            child = child + 1; // 则将子节点的索引位置设置为右子节点的索引位置(即选择右子节点作为下一个比较的节点)
        }
        if (hp->_a[child] < hp->_a[parent]) // 如果子节点的值小于父节点的值(说明不符合堆的性质要求)
        {
            Swap(hp->_a, child, parent); // 则交换子节点和父节点的值(即调整子节点的值到符合要求的位置)并继续向下调整或者循环结束(即完成了一次向下调整)
            parent = child; // 将子节点设为父节点,重新开始向下遍历下一层节点
        }
        else
        {
            break; // 如果符合堆的性质要求,则跳出循环
        }
    }
}


9.堆的删除函数

// 堆的删除函数,删除堆顶元素(最大堆中的最大值或最小堆中的最小值)
void HeapPop(Heap* hp)
{
    assert(hp); // 断言检查hp是否为空指针,如果为空则直接返回或抛出异常
    assert(hp->_a); // 断言检查堆的元素指针数组是否为空指针,如果为空则直接返回或抛出异常
    Swap(hp->_a, hp->_size - 1, 0); // 将堆顶元素和最后一个元素交换位置
    AdjustDown(hp); // 调整堆的性质以满足堆的要求
    hp->_size--; // 堆的元素个数自减
}


10.取堆顶的元素

// 取堆顶的数据,即返回堆中的第一个元素的值
HPDataType HeapTop(Heap* hp)
{
    assert(hp); // 断言检查hp是否为空指针,如果为空则直接返回或抛出异常
    assert(hp->_a); // 断言检查堆的元素指针数组是否为空指针,如果为空则直接返回或抛出异常
    return hp->_a[0]; // 返回堆的第一个元素的值
}

11.堆的数据个数

/ 堆的数据个数,即返回堆中的元素个数
int HeapSize(Heap* hp)
{
    assert(hp); // 断言检查hp是否为空指针,如果为空则直接返回或抛出异常
    assert(hp->_a); // 断言检查堆的元素指针数组是否为空指针,如果为空则直接返回或抛出异常
    return hp->_size; // 返回堆的元素个数
}

12。堆的判空

// 堆的判空,判断堆是否为空
bool HeapEmpty(Heap* hp)
{
    assert(hp); // 断言检查hp是否为空指针,如果为空则直接返回或抛出异常
    assert(hp->_a); // 断言检查堆的元素指针数组是否为空指针,如果为空则直接返回或抛出异常
    if (hp->_size == 0) // 如果堆的元素个数为0
    {
        return false; // 则堆为空,返回false
    }
    else
    {
        return true; // 否则堆不为空,返回true
    }
}

文章来源:https://blog.csdn.net/2301_76653277/article/details/135222875
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。