C语言数据结构之二叉堆

愿你千山暮雪

海棠依旧

不为岁月惊扰平添忧愁


🎥前期回顾-二叉树

🔥数据结构专栏

期待小伙伴们的支持与关注!!!


目录

前期回顾

二叉堆的概念及结构 

 二叉堆的创建

顺序表的结构声明

 顺序表的创建与销毁

二叉堆的插入 

 二叉堆显示堆顶元素

 二叉堆的删除

 二叉堆的判空

整体代码实现

前期回顾

二叉树的顺序结构 

普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。而我们今天学的  总是一棵 完全二叉树

作为一棵完全二叉树,二叉堆可以用一个1-n 的数组来存储

对于双亲节点pp*2+1即为左孩子,p*2+2即为右孩子

对于孩子节点 child 求双亲结点 parent 即 parent = (child - 1) / 2

同时,用size记录当前二叉堆中节点的个数

顺序存储的结论

如果 i 为0,则 i 表示的节点为根节点,否则 i 节点的双亲节点为 (i - 1)/2
如果 2 * i + 1 小于节点个数,则节点 i 的左孩子下标为 2 * i + 1,否则没有左孩子
如果 2 * i + 2 小于节点个数,则节点 i 的右孩子下标为 2 * i + 2,否则没有右孩子

二叉堆(Binary Heap)是最简单、常用的堆,是一棵符合堆的性质的 完全二叉树

它可以实现 O(log⁡n) 的 插入 删除 某个值,并且 O(1) 地查询 最大(或最小)值

二叉堆的概念及结构 

概念#

堆在运用范围上:用来在一组变化频繁(发生增删查改的频率较高)的数据集中查找最值

堆在物理层面上:表现为一组连续的数组区间 long[ ] array 将整个数组看作是堆

堆在逻辑结构上:一般被视为是一颗完全二叉树

结构#

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

堆的性质#

堆中某个节点的值总是不大于或不小于其双亲节点的值
堆总是一棵完全二叉树
小根堆#

<1>双亲节点的值 小于或等于 子节点的值

大根堆#

<2>双亲节点的值 大于或等于 子节点的值

 二叉堆的创建

下面以 小根堆 进行演示

顺序表的结构声明

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>typedef int HeapDataType;typedef struct Heap
{int size;int capacity;HeapDataType* data;
}HP;

 顺序表的创建与销毁

创建

void HPInit(HP* php)
{assert(php);php->data = NULL;php->capacity = php->size = 0;
}

销毁

void HPDestroy(HP* php)
{assert(php);free(php->data);php->data = NULL;php->capacity = 0;php->size = 0;
}

二叉堆的插入 

 顺序表的基本结构

void HPPush(HP* php, HeapDataType x)
{assert(php);if (php->capacity == php->size){int newcapacity = php->capacity == 0 ? 4 : 2 * php->capacity;HeapDataType* newNode = (HeapDataType*)realloc(php->data, sizeof(HeapDataType) * newcapacity);if (newNode == NULL){perror("realloc");exit(-1);}php->data = newNode;   php->capacity = newcapacity;}php->data[php->size] = x;php->size++;AdjustUp(php->data, php->size - 1);
}

交换函数 交换孩子与双亲之间的位置

void Swap(HeapDataType* n, HeapDataType* m)
{HeapDataType tmp = *n;*n = *m;*m = tmp;  
}

我们以下面这两颗树为例

如何 插入 元素,同时维护二叉堆的性质?

如果我们想在二叉堆中插入一个数 11 或者 10

我们可以采用上调算法:该子节点不断与双亲节点比较,如果比双亲节点 就与之交换

直到不大于双亲节点或成为根节点为止

上调算法

void AdjustUp(HeapDataType* data, int child)
{// 找到child的双亲int parent = (child - 1) / 2;while (child > 0){if (data[child] < data[parent]){// 如果孩子小于双亲就进行交换Swap(&data[child], &data[parent]);// 关系转换child = parent;// 需要继续向上调整parent = (parent - 1) / 2;}else{break;}}
}

 二叉堆显示堆顶元素

HeapDataType HPTop(HP* php)
{assert(php);return php->data[0];
}

 二叉堆的删除

 顺序表的基本结构

void HPPop(HP* php)
{assert(php);assert(php->size > 0);// 堆顶元素与末尾元素进行交换,删除最后一个位置Swap(&php->data[0], &php->data[php->size-1]);php->size--;// 针对堆顶位置,做向下调整AdjustDown(php->data, php->size, 0);
}

如何 删除 堆顶端的元素,同时维护堆的性质?


实际的操作是将堆顶元素对堆中最后一个元素交换,堆的元素个数-1,然后再从根结点开始进行一次从上向下的调整。调整时先在左右孩子结点中找最小的,如果双亲结点比这个最小的子结点还小说明不需要调整了,反之将双亲结点和它交换后再考虑后面的结点。

下调算法

确定目标子节点: 

parent 从根节点开始,假设目标子节点为左孩子 child = parent*2+1 ,当左孩子在数组范围之内时进入循环

如果 child + 1 (即右孩子)没超过范围 且 data[child+1] < data[child],则说明右孩子为目标子节点,更新目标子节点 child = child + 1

更新目标亲子关系:

此时 child 为目标子节点,然后再与 parent 双亲结点比较判断是否交换

void AdjustDown(HeapDataType* data, int n, int parent)
{int child = parent * 2 + 1;while (child < n){// 兄弟比较,取小为childif (child + 1 < n && data[child+1] < data[child]){child++;}if (data[child] < data[parent]){// 父子比较,判断是否交换Swap(&data[child], &data[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}
<1>将堆顶元素对堆中最后一个元素交换
<2>将堆中有效数据个数减少一个
<3>对堆顶元素进行向下调整

 二叉堆的判空

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

整体代码实现

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>typedef int HeapDataType;typedef struct Heap
{int size;int capacity;HeapDataType* data;
}HP;void HPInit(HP* php)
{assert(php);php->data = NULL;php->capacity = php->size = 0;
}void HPDestroy(HP* php)
{assert(php);free(php->data);php->data = NULL;php->capacity = 0;php->size = 0;
}void Swap(HeapDataType* n, HeapDataType* m)
{HeapDataType tmp = *n;*n = *m;*m = tmp;  
}void AdjustUp(HeapDataType* data, int child)
{int parent = (child - 1) / 2;while (child > 0){if (data[child] < data[parent]){Swap(&data[child], &data[parent]);child = parent;parent = (parent - 1) / 2;}else{break;}}
}void HPPush(HP* php, HeapDataType x)
{assert(php);if (php->capacity == php->size){int newcapacity = php->capacity == 0 ? 4 : 2 * php->capacity;HeapDataType* newNode = (HeapDataType*)realloc(php->data, sizeof(HeapDataType) * newcapacity);if (newNode == NULL){perror("realloc");exit(-1);}php->data = newNode;   php->capacity = newcapacity;}php->data[php->size] = x;php->size++;AdjustUp(php->data, php->size - 1);
}HeapDataType HPTop(HP* php)
{assert(php);return php->data[0];
}void AdjustDown(HeapDataType* data, int n, int parent)
{int child = parent * 2 + 1;while (child < n){if (child + 1 < n && data[child+1] < data[child]){child++;}if (data[child] < data[parent]){Swap(&data[child], &data[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}void HPPop(HP* php)
{assert(php);assert(php->size > 0);Swap(&php->data[0], &php->data[php->size-1]);php->size--;AdjustDown(php->data, php->size, 0);
}bool HPEmpty(HP* php)
{assert(php);return php->size == 0;
}int main()
{int a[] = { 60,70,65,50,32,100 };HP hp;HPInit(&hp);for (int i = 0; i < sizeof(a) / sizeof(int); i++){HPPush(&hp, a[i]);}while (!HPEmpty(&hp)){printf("%d ", HPTop(&hp));HPPop(&hp);}HPDestroy(&hp); return 0;
}

代码测试

二叉堆性能堆是为了实现排序而实现的一种数据结构,它并不是面向查找操作的,因为在堆中查找一个节点需要进行遍历,其平均时间复杂度是O(n)

不过在 插入 删除 某个值 和求二叉树中最值 往往具有很强的高效性

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

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

相关文章

qtCreator可以全局包含。VSqt中千万不能全局包含,你的控件头文件会自己变成<>括号,编译就报错

qtCreator可以全局包含。 VSqt中千万不能全局包含&#xff0c;你的控件头文件会自己变成&#xff1c;&#xff1e;括号&#xff0c;编译就报错

【fastllm】学习框架,本地运行,速度还可以,可以成功运行chatglm2模型,估计chatglm3模型应该也可以运行,但是并没有现成的模型文件

1&#xff0c;关于 fastllm 项目 https://github.com/ztxz16/fastllm &#x1f680; 纯c实现&#xff0c;便于跨平台移植&#xff0c;可以在安卓上直接编译 &#x1f680; ARM平台支持NEON指令集加速&#xff0c;X86平台支持AVX指令集加速&#xff0c;NVIDIA平台支持CUDA加速…

pytest教程-15-多个fixture以及重命名

领取资料&#xff0c;咨询答疑&#xff0c;请➕wei: June__Go 上一小节我们学习了fixture的yield关键字&#xff0c;本小节我们讲解一下使用多个fixture的方法。 使用多个fixture 如果用例需要用到多个fixture的返回数据&#xff0c;fixture也可以return一个元组、list或字…

数据中台驱动:高效交付之道

如何保证数据中台高效交付&#xff1f; 在数据行业中&#xff0c;项目交付难题尤为突出&#xff0c;尤其在数据中台领域。数据中台项目交付面临诸多挑战&#xff0c;若不妥善解决&#xff0c;将会降低服务质量&#xff0c;影响企业数字化建设的顺利开展&#xff0c;甚至影响项目…

容器(0)-DOCKERFILE-安装-常用命令-部署-迁移备份-仓库

1.安装 启动 systemclt start docker //启动 systemctl status docker //状态 docker info systemclt stop docker systemctl status docker systemctl enable docker //开机启动 2.常用命令 镜像查看 docker images 镜像查看 docker status 镜像拉取 docker pull centos:…

Git 远程操作

1.分布式版本控制系统 我们目前所说的所有内容&#xff08;工作区&#xff0c;暂存区&#xff0c;版本库等等&#xff09;&#xff0c;都是在本地&#xff01;也就是在你的笔记本或者计算机上。而我们的 Git 其实是分布式版本控制系统&#xff01;什么意思呢 可以简单理解为&am…

Reset Verification IP

Reset Verification IP IP 参数及接口 IP 例化界面 相关函数 assert_reset //置位复位信号 < hierarchy_path>.assert_reset();deassert_reset //取消置位复位信号 < hierarchy_path>.deassert_reset();set_master_mode //设置 RST_VIP 模式为 Master < hi…

仿射变换下的点位纠偏

点位偏差一直是一个很头疼的问题&#xff0c;但是由于摄像头和实际环境的局限性&#xff0c;我们不得不面对这个问题。对此&#xff0c;使用判别的方式进行一个仿射变换&#xff0c;是一种非常有效的方式&#xff0c;下图中图1是基准图&#xff0c;图2是目标图&#xff0c;图3是…

【嵌入式高级C语言】11:C语言Makefile

文章目录 1 makefile的概述【只针对Linux有效】1.1 make1.2 makefile1.3 采用makefile的好处 2 Makefile的语法规则3 makefile变量3.1 自定义变量3.2 系统环境变量3.3 预定义变量 4 伪目标5 最终版本Makefile 1 makefile的概述【只针对Linux有效】 1.1 make make是个命令&…

CesiumJS开发中坐标系的相关知识

在Cesium中,坐标系主要涉及两个概念:地球固定坐标系(Earth-Fixed Frame)和参考椭球体坐标系(Reference Ellipsoid Frame)即平时我们用的坐标系: 地球固定坐标系(Earth-Fixed Frame): 地球固定坐标系是指以地球为基准的坐标系,其原点位于地球质心,坐标轴与地…

数据结构之八大排序

&#x1d649;&#x1d65e;&#x1d658;&#x1d65a;!!&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦ &#x1f44f;&#x1f3fb;‧✧̣̥̇:Solitary_walk ⸝⋆ ━━━┓ - 个性标签 - &#xff1a;来于“云”的“羽球人”。…

npm 操作报错记录1- uninstall 卸载失效

npm 操作报错记录1- uninstall 卸载失效 1、问题描述 安装了包 vue/cli-plugin-eslint4.5.0 vue/eslint-config-prettier9.0.0 但是没有使用 -d &#xff0c;所以想重新安装&#xff0c;就使用 uninstall 命令卸载&#xff0c;结果卸载了没反应&#xff0c;也没有报错&#xf…

【Python】成功解决AttributeError: ‘MyClass‘ object has no attribute ‘my_attribute‘

【Python】成功解决AttributeError: ‘MyClass’ object has no attribute ‘my_attribute’ &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门…

CRM术语速览:掌握这十个专业名词,成为CRM专家

无论您是销售人员还是采购经理&#xff0c;熟悉CRM管理系统专业术语都是一门必修课。擅于运用CRM专业术语帮助您理解CRM管理系统的功能、更好的开展业务。本文与您分享不得不知道的十大CRM专业术语&#xff0c;CRM常用术语合集。常见的CRM术语包括MQL、SQL、SDR、销售漏斗等等。…

【Docker】了解Docker Desktop桌面应用程序,TA是如何管理和运行Docker容器(3)

欢迎来到《小5讲堂》&#xff0c;大家好&#xff0c;我是全栈小5。 这是《Docker容器》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 特别是针对知识点的概念进行叙说&#xff0c;大部分文章将会对这些概念进行实际例子验证&#xff0c;以此达到加深对…

AI新工具 百分50%算力确达到了GPT-4水平;将音乐轨道中的人声、鼓声、贝斯等音源分离出来等

1: Pi 百分50%算力确达到了GPT-4水平 Pi 刚刚得到了巨大的升级&#xff01;它现在由最新的 LLMInflection-2.5 提供支持&#xff0c;它在所有基准测试中都与 GPT-4 并驾齐驱&#xff0c;并且使用不到一半的计算来训练。 地址&#xff1a;https://pi.ai/ 2: Moseca 能将音乐…

JAVA虚拟机、Dalvik虚拟机和ART虚拟机简要对比

1、什么是JVM&#xff1f; JVM本质上就是一个软件&#xff0c;是计算机硬件的一层软件抽象&#xff0c;在这之上才能够运行Java程序&#xff0c;JAVA在编译后会生成类似于汇编语言的JVM字节码&#xff0c;与C语言编译后产生的汇编语言不同的是&#xff0c;C编译成的汇编语言会…

【Web安全】htaccess攻击

.htaccess攻击 文章目录 .htaccess攻击1. .htaccess文件2. 常见用法2.1. 自定义出错界面2.2. 强制文件执行方式2.3. PCRE绕过正则匹配2.4. php_value修改php设定2.5. php_value文件包含2.6. 把htaccess当作php 1. .htaccess文件 .htaccess是Apache网络服务器一个配置文件&#…

【面试精讲】Java动态代理是如何实现的?JDK Proxy 和 CGLib 有什么区别?

Java动态代理是如何实现的&#xff1f;JDK Proxy 和 CGLib 有什么区别&#xff1f; 目录 一、Java动态代理的实现 1、使用JDK Proxy实现动态代理 2、使用CGLib实现动态代理 二、JDK Proxy 与 CGLib 的区别 三、Spring中的动态代理 四、 Lombok代理原理 总结 前言 本文…

21 easy 1. 两数之和

//给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 // // 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在答案里不能重复出现。 // // 你可以…