数据结构--堆(C语言实现,超详细!!!)

堆的定义

堆的概念解释

   堆(Heap)是一种非常有用的数据结构,特别在需要频繁地进行插入和删除最大或最小元素的操作中表现出色。但请注意,这里的“堆”与编程语言中用于动态内存分配的“堆”是两个完全不同的概念。
   简单地说,堆是一种特殊的树形数据结构,它满足堆属性:即任意节点都小于或等于(在最大堆中)或大于或等于(在最小堆中)其子节点。最常见的堆是二叉堆,它是一种完全二叉树,这意味着除了最后一层之外,每一层都是完全填满的,而且最后一层的元素都是靠左对齐的,是连续的。
最

最大堆和最小堆

   最大堆:在最大堆中,父节点的值总是大于或等于其子节点的值。因此,根节点(也就是堆顶元素)总是包含堆中的最大值。
最小堆:与最大堆相反,在最小堆中,父节点的值总是小于或等于其子节点的值。因此,根节点包含堆中的最小值。

堆的应用举例

优先队列:堆是实现优先队列的理想数据结构,因为它能高效地插入新元素并删除最大或最小元素。
堆排序:堆排序算法使用堆数据结构来对数组进行排序,其时间复杂度为O(n log n)。

C语言实现堆

需要包含的头文件

#include<stdio.h>
#include<stdlib.h>//包含realloc()函数,作用是向栈申请一块空间
#include<assert.h>//包含assert()函数,作用是判断()内的内容是否为空
#include<stdbool.h>//包含bool函数,作用是判断返回值是否为0,若为0,则循环继续,若不为零,则跳出循环
#include<string.h>

堆的定义

typedef int HPDataType;//这里将int重命名为HPDataType是为了以后如果要用非int类型的时候方便改动,只需要改这一条代码就行
typedef struct Heap
{
   
   
	HPDataType* a;
	int size;//以下两行代码不用HPDataType声明是因为下标(size)和容量(capacity)一般都是用整数表示
	int capacity;
}HP;//typedef使struct Heap重命名为HP,以便简化后续使用该结构体

堆的初始化

该函数通过断言确保传入的指针不是空的,并将结构体的三个成员分别初始化为 NULL、0 和 0,以确保在使用该结构体之前,它处于一个安全、已知的状态。

// 函数定义:初始化一个名为HP的结构体  
// 参数:一个指向HP结构体的指针 php  
void HPInit(HP* php)  
{
   
     
    // 断言:确保传入的指针 php 不是空指针  
    // 如果 php 是空指针,程序会在这里终止,并打印错误信息  
    assert(php);  
  
    // 将HP结构体的 a 成员初始化为 NULL  
    // 这通常是为了确保在使用前该指针是安全的,避免野指针问题  
    php->a = NULL;  
  
    // 将HP结构体的 size 成员初始化为 0  
    // size 表示当前HP结构体中存储的元素数量  
    php->size = 0;  
  
    // 将HP结构体的 capacity 成员初始化为 0  
    // capacity 表示HP结构体可以存储的元素数量的最大值  
    php->capacity = 0;  
}

堆的销毁

以下代码定义了一个函数 HPDestroy,其作用是销毁一个HP结构体实例。具体来说,它首先检查传入的指针是否为空,然后释放该结构体中a成员指向的内存空间(这通常是一个动态分配的数组),并将a成员置为NULL以防止野指针。接着,它将capacity和size成员都设置为0,以表明该结构体当前没有分配任何内存,也没有存储任何元素。这是一个重要的安全实践同时也是良好的编程习惯,用于确保在销毁堆数据结构后不会留下任何可能导致错误或未定义行为的引用。

// 函数定义:销毁一个名为HP的结构体 
// 参数:一个指向HP结构体的指针 php  
void HPDestroy(HP* php)  
{
   
     
    // 断言:确保传入的指针 php 不是空指针
    //只要是参数是指针都需要判断一下该指针是不是为空,这是一个良好的习惯  
    // 如果 php 是空指针,程序会在这里终止,并打印错误信息  
    assert(php);  
  
    // 释放HP结构体中a成员所指向的内存空间  
    // a成员可能是一个动态分配的数组或其他数据结构  
    // 注意:释放内存后,为了避免野指针,需要将指针置为NULL  
    free(php->a);  
      
    // 将a成员设置为NULL,以防止野指针的产生  
    // 这是一个重要的安全步骤,确保之后不会错误地访问已经释放的内存  
    php->a = NULL;  
  
    // 将capacity成员设置为0,表示当前没有分配任何容量  
    // 这是一个状态标记,用于表明内存已经被释放  
    php->capacity = 0;  
  
    // 将size成员设置为0,表示当前没有存储任何元素  
    // 与capacity一起,这两个值提供了关于HP结构体当前状态的清晰指示  
    php->size = 0;  
}

交换函数

void swap(int* px, int* py) {
   
   //如果不传指针则交换不成功
	int a = *px;
	*px = *py;
	*py = a;
}

堆的向上调整

这里使用的是左右孩子表示法,即左结点为左孩子,右结点为右孩子。
父子储存的下标规律:(左孩子)leftchild=parent x 2+1;(右孩子)rightchild=parent x 2+2;
parent=(child-1)/2,这里的child可以是leftchild也可以是rightchild。

void AdjustUp(HPDataType*a, int child) {
   
   //注意没有HP*php
	int parent = (child - 1) / 2;//这里是已知parent找child
	//while(parent>=0)
	while (child > 0) {
   
   

		if (a[child] > a[parent]) {
   
   
			swap(&a[child], &a[parent]);
			child = parent;//第八行
			parent = (child - 1) / 2;//第九行
		}
		else {
   
   
			break;
		}
	}
}

第八行:这里是已知child找parent,因为是向上调整,找到parent并且交换值后这个parent就是上面那个结点的child,这里的parent和child可以代表身份和下标,身份可以通过child = parent;赋值,这里是将parent的身份变成上面那个结点的child,然后再通过第九行用这个新的child去

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值