堆的实现(C语言版)

在这里插入图片描述

文章目录

  • 概述
  • 堆的实现
    • 初始化
    • 销毁
    • 插入
    • 删除
    • 取堆顶元素
    • 求堆的长度
    • 判断堆是否为空
  • 完整代码

概述

如果有一个关键码的集合K = {k0,k1,k2…kn-1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:Ki <=K2*i+1 且 Ki<=K2*i+2 (Ki >= K2*i+1且 Ki>= K2*i+1) i = 0,1,2…,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。堆的性质:

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

在这里插入图片描述

堆的实现

初始化

堆的存储结构是一个数组,堆的初始化需要定义一个数组,当前元素个数和容量。和顺序表的初始化一样。

typedef struct Heap
{HPDataType* a;int size;int capacity;
}HP;void HeapInit(HP* php)
{assert(php);php->a = NULL;php->size = php->capacity = 0;
}

销毁

释放数组a的空间,将php->capacity = php->size = 0

void HeapDestroy(HP* php)
{assert(php);free(php->a);php->capacity = php->size = 0;
}

插入

堆的插入是先在数组的最后插入元素,但是需要满足堆的特点(大堆或小堆),因此需要用到向上调整算法,来实现这一特点。

介绍向上调整算法

这里小编以实现小堆为例

在数组的最后插入一个元素child,然后这个元素与其双亲节点parent进行比较:

  • 如果 child>parent:满足小堆的条件,无需交换
  • 如果 child<parent:不满足小堆条件,此时需要将孩子节点child与它的双亲结点parent进行交换,此时原来的双亲结点parent变成了孩子结点child,原来的孩子节点child变成了双亲结点parent。此时,再让现在的双亲结点parent和它的双亲结点parent进行比较,如果不满足小堆,则继续交换,继续比较
  • 循环结束的条件是child>0

举个例子:

如下,在堆中插入元素10:

在这里插入图片描述
将10与它的双亲结点进行比较,10<28,不满足小堆的条件,将10和28,进行交换:

在这里插入图片描述

交换完后,此时的10变成了28的双亲结点,28变成了10的孩子结点。现在再将10与它的双亲结点比较,10<18,不满足小堆的特点,继续交换。

在这里插入图片描述

交换完后10变成了18的双亲结点,18变成了10的孩子结点。10和它的双亲结点比较,依然不满足小堆条件,继续交换

在这里插入图片描述

此时,10已经变成了根节点,并且满足小堆的条件,循环结束。

看了图解,对向上调整算法有了大概的印象,但是代码的编写,还需要再去分析一下。

定义parent是孩子的双亲结点,双亲结点parent与孩子结点child满足parent = (child - 1) / 2关系。进入循环,比较孩子节点的值和双亲结点的值,判断是否满足小堆的条件。

void swap(HPDataType* p1, HPDataType* p2)
{int 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;}}
}

写完向上调整算法,便可实现插入操作

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, newCapacity * sizeof(HPDataType));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);
}

删除

在删除操作里面,一般规定删除堆顶,即根节点

在这里插入图片描述

删除根节点的常规操作是将根结点和最后一个叶节点进行交换,然后尾删即可,此时根节点的左右子树依然是小堆

在这里插入图片描述
但是根节点不满足小队的条件,因此引入向下调整算法

向下调整算法:

向上调整算法是一个道理

但是此时根节点是双亲结点,有两个孩子,不知道该选择哪一个孩子。这里使用到了假设法:假设左孩子小,如果假设错了,更新一下

判断双亲结点和孩子结点的大小:

  • 如果双亲结点小于孩子结点,直接结束
  • 如果双亲结点大于孩子结点,交换双亲结点和孩子结点的值,然后更新一下双亲结点的位置和孩子节点的位置

循环结束的条件是child<size

和向上调整算法基本一致,直接上代码:

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

删除操作:

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

取堆顶元素

先判断堆是否存在,然后直接返回堆顶元素即可

HPDataType HeapTop(HP* php)
{assert(php);assert(php->size > 0);return php->a[0];
}

求堆的长度

先判断堆是否存在,直接返回堆的长度即可

size_t HeapSize(HP* php)
{assert(php);return php->size;
}

判断堆是否为空

先判断堆是否存在,如果php->size==0,那么堆为空,返回true,反之返回false

bool HeapEmpty(HP* php)
{assert(php);return php->size == 0;
}

完整代码

Heap.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>typedef int HPDataType;typedef struct Heap
{HPDataType* a;int size;int capacity;
}HP;void HeapInit(HP* php);
void HeapDestroy(HP* php);
void HeapPush(HP* php, HPDataType x);
// 规定删除堆顶(根节点)
void HeapPop(HP* php);
HPDataType HeapTop(HP* php);
size_t HeapSize(HP* php);
bool HeapEmpty(HP* php);

Heap.c

# define _CRT_SECURE_NO_WARNINGS#include"Heap.h"void HeapInit(HP* php)
{assert(php);php->a = NULL;php->size = php->capacity = 0;
}void HeapDestroy(HP* php)
{assert(php);free(php->a);php->capacity = php->size = 0;
}void swap(HPDataType* p1, HPDataType* p2)
{int 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;}}
}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, newCapacity * sizeof(HPDataType));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);
}AdjustDown(HPDataType* a,int size, int parent)
{int child = parent * 2 + 1;while (child < size){if (a[child] > a[child + 1] && child + 1 < size){child++;}if (a[child] < a[parent]){swap(a[parent], a[child]);parent = child;child = child * 2 + 1;}else{break;}}
}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);
}HPDataType HeapTop(HP* php)
{assert(php);assert(php->size > 0);return php->a[0];
}size_t HeapSize(HP* php)
{assert(php);return php->size;
}bool HeapEmpty(HP* php)
{assert(php);return php->size == 0;
}

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【SpringBoot系列】SpringBoot日志配置

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

【MySQL系列】PolarDB入门使用

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

OpenMLDB v0.8.4 诊断工具全面升级

新的v0.8.4版本中&#xff0c;我们对于诊断工具进行了全面系统化的升级&#xff0c;以提供更加完整和智能化的诊断报告&#xff0c;有助于高效排查 OpenMLDB 集群问题&#xff0c;大幅提升运维效率。 相比于之前的版本&#xff0c;新的诊断工具增添一键诊断功能&#xff0c;使…

Python,FastAPI,mLB网关,无法访问/docs

根源就是js和ccs文件访问路由的问题&#xff0c;首先你要有本地的文件&#xff0c;详情看https://qq742971636.blog.csdn.net/article/details/134587010。 其次&#xff0c;你需要这么写&#xff1a; /unicontorlblip就是我配置的mLB网关路由。 app FastAPI(titleoutpaint…

mac添加Chrome插件的方法

如果是.crx的插件 更改后缀crx为zip 后续步骤同下文.zip文件 如果是.zip的插件 使用终端进行解压 注意不要用解压工具解压&#xff0c;一定要用终端&#xff0c;命令行解压 // 进入到“插件名.zip”文件的目录下&#xff0c;输入下面命令&#xff1a; unzip 插件名.zip -…

虾皮自己养一批买家号的优势有哪些?

测评自养号说简单点就是虾皮卖家自己搭建国外的环境来注册的买家号&#xff0c;可以自己养号下单&#xff0c;对自己产品进行产品优点、卖家服务态度以及物流收货体验等等留下优质的评论&#xff0c;可以帮助店铺产品快速建立买家的信任&#xff0c;提高产品的曝光和排名&#…

中断方式的数据接收

中断接收简介 回顾之前的代码 之前的代码是 等待标志位RXNE位为1才有数据 进而读取数据存放在变量c中 再根据c变量的数据是为0还是为1进而编写灯亮灭的代码 if语句 但这样的代码明显不符合裸机多任务的编程模型 因为在while中为进程 进程执行的时间不能大于5ms 但是while&…

毛里塔尼亚市场开发攻略,收藏一篇就够了

毛里塔尼亚是非洲西北部的一个国家&#xff0c;也是中国长期援建的一个国家&#xff0c;也是一带一路上的国家。毛里塔尼亚生产生活资料依赖进口&#xff0c;长期依赖跟我们国家的贸易关系也是比较紧密的&#xff0c;今天就来给大家介绍一下毛里塔尼亚的市场开发公路。文章略长…

188. 股票买卖问题(交易次数为任意正整数)

题目 题解 class Solution:def maxProfit(self, k: int, prices: List[int]) -> int:N len(prices)# 定义状态:dp[i][j][k]表示在第i天&#xff0c;有j次交易机会&#xff0c;持有或不持有的最大利润dp [[[0 for i in range(2)] for j in range(k1)] for m in range(N)]f…

2023 hnust 湖南科技大学 信息安全管理课程 期中考试 复习资料

前言 ※老师没画重点的补充内容★往年试卷中多次出现或老师提过的&#xff0c;很可能考该笔记是奔着及格线去的&#xff0c;不是奔着90由于没有听过课&#xff0c;部分知识点不一定全&#xff0c;答案不一定完全正确 题型 试卷有很多题是原题 判断题&#xff08;PPT&#xff…

2023.11.23使用flask实现在指定路径生成文件夹操作

2023.11.23使用flask实现在指定路径生成文件夹操作 程序比较简单&#xff0c;实现功能&#xff1a; 1、前端输入文件夹 2、后端在指定路径生成文件夹 3、前端反馈文件夹生成状态 main.py from flask import Flask, request, render_template import osapp Flask(__name__)a…

下一代ETL工具:微服务架构的全新数据集成平台

当前对于大型企业来说数据的整合和加工变得越来越重要。随着业务需求的不断增长&#xff0c;企业数据量越来越大&#xff0c;数据管道越来越多&#xff0c;现有的ETL&#xff08;抽取、转换、加载&#xff09;工具已不再满足实时、高性能和微服务架构等现代化需求。因此&#x…