[C/C++]数据结构 堆的详解

一:概念

        堆通常是一个可以被看做一棵完全二叉树的数组对象,它是一颗完全二叉树,堆存储的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并且需要满足每个父亲结点总小于其子节点(或者每个父亲结点总大于其子节点)

        堆可以分为两种:

  • 小堆: 任意一个父亲节点都小于其子节点
  • 大堆:任意一个父亲节点都大于其子节点

二:堆的定义

       堆是一个完全二叉树,但是其物理逻辑为数组

typedef int HPDataType;
typedef struct heap
{HPDataType* a;int size;      int capacity;  //数组容量
}HP;

 接口:

//堆的初始化
void InitHeap(HP* hp);//在堆上入一个数据使其还是堆
void HeapPush(HP* hp, HPDataType x);//在堆上出一个数据使其还是堆
void HeapPop(HP* hp);//取堆头元素
HPDataType HeapTop(HP* hp);//判断堆是否为空
bool HeapEmpty(HP* hp);//求堆的长度
int HeapSize(HP* hp);//堆的销毁
void DestoryHeap(HP* hp);

三:接口实现

        堆的实现许多操作和顺序表的实现相同,忘记顺序表的烙铁可以复习一下顺序表-->[C/C++]数据结构----顺序表的实现(增删查改) ,我们以小堆的实现为例:

1.堆的初始化

void InitHeap(HP* hp)
{hp->a = NULL;hp->size = 0;hp->capacity = 0;
}

2.判断堆是否为空

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

3.堆的销毁

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

4.返回堆头元素

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

5.堆的大小

int HeapSize(HP* hp)
{assert(hp);return hp->size;
}

到重点啦!!!

ps:下面的向下和向上调整法都是按小堆实现的,若要按大堆实现只需改下大小比较符号即可

6.入堆(在堆里面插入一个数据,使其还是堆)

      先将数据插入到堆的末尾,如果堆的性质被破坏的话,就让该节点与其父亲结点交换,向上调整,直到满足堆的性质

        例如:在小堆后面插入数据10

由于10小于他的父亲结点,所以应该让10和其父亲结点交换

此时10仍然小于其父亲节点,继续交换

仍然不满足堆的性质,继续交换

向上调整完成

这些数据的存储逻辑上堆为二叉树结构,实际上数据储存在数组中,向上调整法涉及到了如何通过孩子结点找到双亲结点,其实这里有一些结论可以通过一方的下标找到另一方的下标

  • parent = (child-1)/2
  • leftchild = parent*2+1
  • rightchild = parent*2+2

        向上调整法代码实现:

void AdjustUP(HP* hp, int child)
{int parent = (child - 1) / 2;while (child > 0) //最坏的情况就是需要调整到下标为0的位置{if (hp->a[parent] > hp->a[child]){swap(&hp->a[parent], &hp->a[child]);child = parent;parent = (parent - 1) / 2;}else{//由于插入数据前本来就是堆,如果不满足上述条件,说明所有数据已经满足堆的性质了break;}}
}

入堆:

void HeapPush(HP* hp, HPDataType x)
{assert(hp);//判断是否需要扩容if (hp->size == hp->capacity){int newcapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;HPDataType* ret = (HPDataType*)realloc(hp->a, sizeof(HPDataType) * newcapacity);if (ret == NULL){perror("realloc");exit(-1);}hp->a = ret;hp->capacity = newcapacity;}//尾插数据hp->a[hp->size] = x;hp->size++;//向上调整AdjustUP(hp, hp->size - 1);
}

7.堆的删除

        堆的删除默认是删除堆顶的元素,如果直接删除堆顶的元素,再将其孩子结点中较小的结点作为堆顶的话,会导致大小关系错乱,因为本来堆顶的左右子树就是堆,按刚刚讲的方法,其堆结构就会被破坏,还需要重新建堆调整,这样的消耗太大了,相反,如果删除一个堆尾的元素消耗却很小,所以可以按如下方法:

  1. 把堆顶元素和堆尾元素交换
  2. 删除堆尾元素
  3. 将堆头元素向下调整到合适位置

向下调整法代码实现:

void AdjustDown(HPDataType* a, int size, int parent)
{//这里假设左孩子是两孩子里面较大的HPDataType child = parent * 2 + 1;while (child<size){    //验证假设是否成立,不成立则更新孩子结点//右边的条件是为了排除数组访问越界的情况,child+1是右孩子下标if (a[child + 1] < a[child] && (child + 1) < size){child = child + 1;}//如果父亲结点比孩子结点大,交换if (a[parent] > a[child]){swap(&a[parent], &a[child]);parent = child;child = child * 2 + 1;}else{break;}}
}

堆得删除:

void HeapPop(HP* hp)
{assert(hp);swap(&hp->a[0], &hp->a[hp->size - 1]);hp->size--;AdjustDown(hp->a, hp->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 InitHeap(HP* hp);
void HeapPush(HP* hp, HPDataType x);
void HeapPop(HP* hp);
HPDataType HeapTop(HP* hp);
bool HeapEmpty(HP* hp);
int HeapSize(HP* hp);
void DestoryHeap(HP* hp);

heap.c: 用于函数的实现

#define  _CRT_SECURE_NO_WARNINGS 1		
#include"heap.h"void InitHeap(HP* hp)
{hp->a = NULL;hp->size = 0;hp->capacity = 0;
}void DestoryHeap(HP* hp)
{assert(hp);free(hp->a);hp->size = 0;hp->capacity = 0;
}void swap(HPDataType* a, HPDataType* b)
{HPDataType tmp = *a;*a = *b;*b = tmp;
}void AdjustUP(HP* hp, int child)
{int parent = (child - 1) / 2;while (child > 0){if (hp->a[parent] > hp->a[child]){swap(&hp->a[parent], &hp->a[child]);child = parent;parent = (parent - 1) / 2;}else{break;}}
}void HeapPush(HP* hp, HPDataType x)
{assert(hp);if (hp->size == hp->capacity){int newcapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;HPDataType* ret = (HPDataType*)realloc(hp->a, sizeof(HPDataType) * newcapacity);if (ret == NULL){perror("realloc");exit(-1);}hp->a = ret;hp->capacity = newcapacity;}hp->a[hp->size] = x;hp->size++;AdjustUP(hp, hp->size - 1);
}HPDataType HeapTop(HP* hp)
{assert(hp);assert(hp->size);return hp->a[0];
}bool HeapEmpty(HP* hp)
{assert(hp);return hp->size == 0;
}int HeapSize(HP* hp)
{assert(hp);return hp->size;
}void AdjustDown(HPDataType* a, int size, int parent)
{HPDataType child = parent * 2 + 1;while (child<size){if (a[child + 1] < a[child] && (child + 1) < size){child = child + 1;}if (a[parent] > a[child]){swap(&a[parent], &a[child]);parent = child;child = child * 2 + 1;}else{break;}}
}void HeapPop(HP* hp)
{assert(hp);swap(&hp->a[0], &hp->a[hp->size - 1]);hp->size--;AdjustDown(hp->a, hp->size, 0);
}

test.c :测试功能

#define  _CRT_SECURE_NO_WARNINGS 1		
#include"heap.h"
int main()
{int a[] = { 9,8,7,6,5,4,3,2,1 };HP hp;InitHeap(&hp);//这里是将数组的数据依次插入形成堆for (int i = 0; i < sizeof(a) / sizeof(int); i++){HeapPush(&hp, a[i]);}//依次打印堆头元素while (!HeapEmpty(&hp)){printf("%d ", HeapTop(&hp));HeapPop(&hp);}return 0;
}

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

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

相关文章

MySQL性能优化,SQL调优,SQL调优的手段

文章目录 对MySQL性能的优化的理解硬件和操作系统层面的优化架构设计层面的优化MySQL程序配置优化SQL优化 SQL调优有哪几种方式1.EXPLAIN2.SQL语句中IN包含的值不应过多3.SELECT语句务必指明字段名称4.当只需要一条数据的时候&#xff0c;使用limit 15.如果排序字段没有用到索引…

二叉树算法—后继节点

与其明天开始&#xff0c;不如现在行动&#xff01; 文章目录 1 后继节点1.1 解题思路1.2 代码实现 &#x1f48e;总结 1 后继节点 1.1 解题思路 二叉树节点结构定义如下&#xff1a; public static class Node { public int cal; public Node left; public Node right; public…

深度学习中的注意力机制:原理、应用与实践

深度学习中的注意力机制&#xff1a;原理、应用与实践 摘要&#xff1a; 本文将深入探讨深度学习中的注意力机制&#xff0c;包括其原理、应用领域和实践方法。我们将通过详细的解析和代码示例&#xff0c;帮助读者更好地理解和应用注意力机制&#xff0c;从而提升深度学习模…

100天精通Python(可视化篇)——第109天:Pyecharts绘制各种常用地图(参数说明+代码实战)

文章目录 专栏导读一、地图应用场景二、参数说明1. 导包2. add函数 三、地图绘制实战1. 省市地图2. 中国地图3. 中国地图&#xff08;带城市&#xff09;4. 中国地图&#xff08;分段型&#xff09;5. 中国地图&#xff08;连续型&#xff09;6. 世界地图7. 行程轨迹地图8. 人口…

WordPress最廉价优化整站的加载速度

为什么说一个站不优化就等于一个人做整个团队的事务导致项目进展慢&#xff0c;网站也是如此 图片、静态文件、php分离加速&#xff0c;加载速度并不是很快但是很协调比单个网站加载速度快许多 一、图片单域名加载设置上传文件路径和域名 以下代码添加在主题目录&#xff1a;fu…

C# WPF上位机开发(开篇)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 之前很少用到c#语言&#xff0c;大部分时间都用c/c&#xff0c;主要是它可以兼顾上位机qt开发以及嵌入式开发。所以&#xff0c;用c/c是比较合理的…

U-boot(五):启动内核

本文主要探讨210的uboot启动内核过程。 嵌入式系统状态启动 未上电时bootloader、kernel、rootfs以镜像形式存储在启动介质中(X210为iNand/SD卡),运行时搬运到DDR中 未上电时u-boot.bin,zImage,rootfs在SD卡中各自对应的分区中,启动时去对应分区寻找(分区表一…

使用STM32与MFRC522 IC进行RFID卡的读取与识别(含代码)

利用STM32与MFRC522 IC进行RFID卡的读取和识别&#xff0c;可以实现对RFID卡的读取和获取卡片标识信息。MFRC522 IC是一种高集成度的13.56MHz RFID芯片&#xff0c;常用于门禁系统、物流跟踪和智能支付等领域。下面将介绍如何使用STM32与MFRC522 IC进行RFID卡的读取和识别&…

光线追踪-Peter Shirley的RayTracingInOneWeekend系列教程(book1-book3)代码分章节整理

自己码完了一遍了&#xff0c;把代码分章节整理了一下&#xff0c;可以按章节独立编译&#xff0c;运行, 也可以直接下载编译好的release版本直接运行。 项目地址&#xff1a; Github: https://github.com/disini/RayTracingInOneWeekendChaptByChapt ​ ​ ​ ​

makefile编写练习

makefile编写练习 OVERVIEW makefile编写练习文件结构直接编译整个项目并运行将项目制作成为静态库将项目制作成为动态库 编写makefile文件来编译带头文件的程序&#xff0c; 文件结构 初始项目文件结构&#xff0c;如下所示&#xff1a; #ifndef ADD_HPP #define ADD_HPPint…

C++前缀和算法:统计美丽子字符串

题目 给你一个字符串 s 和一个正整数 k 。 用 vowels 和 consonants 分别表示字符串中元音字母和辅音字母的数量。 如果某个字符串满足以下条件&#xff0c;则称其为 美丽字符串 &#xff1a; vowels consonants&#xff0c;即元音字母和辅音字母的数量相等。 (vowels * cons…

P18 C++ 继承

目录 前言 01 不使用继承会让你多打很多无用的代码 02 继承 最后的话 前言 本期我们学习 C 面向对象编程中的继承。 面向对象程序设计中最重要的一个概念是继承。继承允许我们依据另一个类来定义一个类&#xff0c;这使得创建和维护一个应用程序变得更容易。这样做&#…