【算法与数据结构】深入解析二叉树(二)之堆结构实现

请添加图片描述

文章目录

  • 📝二叉树的顺序结构及实现
    • 🌠 二叉树的顺序结构
    • 🌠 堆的实现
    • 🌠 堆的实现
      • 🌉堆向下调整算法
      • 🌉堆的创建
      • 🌉建堆时间复杂度
      • 🌉堆的插入
      • 🌉堆的删除
    • 🌠堆向上调整算法
      • 🌉堆的接口
    • 🌠堆的实现
    • 🌠堆的实现代码测试
  • 🚩总结


📝二叉树的顺序结构及实现

🌠 二叉树的顺序结构

普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。
在这里插入图片描述

🌠 堆的实现

堆(heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。堆总是满足下列性质:
堆的物理结构本质上是顺序存储的,是线性的。但在逻辑上不是线性的,是完全二叉树的这种逻辑储存结构。 堆的这个数据结构,里面的成员包括一维数组,数组的容量,数组元素的个数,有两个直接后继。
堆的定义如下:n个元素的序列{k1,k2,ki,…,kn}当且仅当满足下关系时,称之为堆。
(且)或者(), ()
若将和此次序列对应的一维数组(即以一维数组作此序列的存储结构)看成是一个完全二叉树,则堆的含义表明,完全二叉树中所有非终端结点的值均不大于(或不小于)其左、右孩子结点的值。由此,若序列{k1,k2,…,kn}是堆,则堆顶元素(或完全二叉树的根)必为序列中n个元素的最小值(或最大值)。
在这里插入图片描述

将根结点最大的堆叫做最大堆或大根堆,根结点最小的堆叫做最小堆或小根堆。常见的堆有二叉堆、斐波那契堆等
堆的性质:

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

🌠 堆的实现

🌉堆向下调整算法

现在我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆。向下调整算法有一个前提:左右子树必须是一个堆,才能调整。

int array[] = {27,15,19,18,28,34,65,49,25,37};

在这里插入图片描述

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

🌉堆的创建

下面我们给出一个数组,这个数组逻辑上可以看做一颗完全二叉树,但是还不是一个堆,现在我们通过算法,把它构建成一个堆。根节点左右子树不是堆,我们怎么调整呢?这里我们从倒数的第一个非叶子节点的子树开始调整,一直调整到根节点的树,就可以调整成堆。

int a[] = {1,5,3,8,7,6};

在这里插入图片描述

代码:

int size=sizeof(array)/sizeof(int);
//向下建堆,复杂度为O(N)
for (int i = (size - 1 - 1) / 2; i >= 0; i--)
{AdjustDown(array, size,i);
}
void AdjustDown(HPDataType* a, int n, int parent)
{ //a是数组指针,n是数组长度,parent是当前需要下调的父结点索引int child = parent * 2 + 1;//child表示父结点parent的左孩子结点索引,因为是完全二叉堆,可以通过parent和2计算得到while (child < n){//如果左孩子存在if (child + 1 < n && a[child + 1] < a[child]){//如果右孩子也存在,并且右孩子值小于左孩子,则child指向右孩子child++;}if (a[child] < a[parent])//如果孩子结点值小于父结点值,则需要交换{Swap(&a[child], &a[parent]);//交换孩子和父结点parent = child;//父结点下移为当前孩子结点child = parent * 2 + 1;//重新计算新的左孩子结点索引}else{break;}}
}

这是向下调整,最终形成小根堆,如果你想修改大根堆只需改变两个代码方向即可:

if (child+1<n && a[child + 1]>a[child])
if (a[child] > a[parent])

🌉建堆时间复杂度

因为堆是完全二叉树,而满二叉树也是完全二叉树,此处为了简化使用满二叉树来证明(时间复杂度本来看的就是近似值,多几个节点不影响最终结果):

复杂度:O(N)
在这里插入图片描述

🌉堆的插入

先插入一个10到数组的尾上,再进行向上调整算法,直到满足堆。

在这里插入图片描述

void HPPush(HP* php, HPDataType x)
{assert(php);if (php->size == php->capacity){size_t newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;HPDataType * tmp = realloc(php->a, sizeof(HPDataType) * newCapacity);if (tmp == NULL){perror("realloc fail");return;}php->a = tmp;php->capacity = newCapacity;}php->a[php->size] = x;php->size++;AdjustUp(php->a, php->size - 1);
}

🌉堆的删除

删除堆是删除堆顶的数据,将堆顶的数据根最后一个数据一换,然后删除数组最后一个数据,再进行向下调整算法。
在这里插入图片描述

//时间复杂度是:logN
void HPPop(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);
}

🌠堆向上调整算法

堆向上调整算法主要用于堆排序中,删除堆顶元素后,将最后一个元素补至堆顶,然后需要向上调整。

//向上调整,建堆O(N*logN)
for (int i = 1; i < size; i++)
{ //for循环从索引1开始,到size结束,即从第二个元素开始。AdjustUp(array, i);
}
void AdjustUp(HPDataType* a, int child)
{int parent = (child - 1) / 2;//计算父节点的位置:父节点位置 = (当前节点位置-1)/2while (child > 0)//如果当前节点位置大于0,并且当前节点值小于父节点值,需要向上调整:{if (a[parent] < a[child]){Swap(&a[parent], &a[child]);child = parent;parent = (parent - 1) / 2;}//将当前节点位置设为父节点的位置,重复执行步骤2和步骤3//直到当前节点位置为0,或者当前节点值不小于父节点值为止。else{break;}}
}

堆向上调整的主要步骤::确定需要调整的子节点,通常是补至堆顶的最后一个元素。
时间复杂度为O(N*logN)
在这里插入图片描述

🌉堆的接口

# define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
#include<string.h>typedef int HPDataType;typedef struct Heap
{HPDataType* a;int size;int capacity;
}HP;void Swap(int* px, int* py);
void AdjustUp(HPDataType* a, int child);
void AdjustDown(HPDataType* a, int n, int parent);//堆的简单初始化
//void HPInit(HP* php);
//堆的初始化+建堆
void HPInitArray(HP* php, HPDataType* a, int n);
//堆的销毁
void HPDestroy(HP* php);
//堆插入数据然后保持数据是堆
void HPPush(HP* php, HPDataType x);
//取堆顶的数据
HPDataType HPTop(HP* php);
//删除堆数据
void HPPop(HP* php);
//堆的数据个数
int HeapSize(HP* php);
//堆的判空
bool HPEmpty(HP* php);

🌠堆的实现

#include"HeadSort.h"
//堆的简单初始化
void HPInit(HP* php)
{assert(php);php->a = NULL;php->size = 0;php->capacity = 0;
}void HPInitArray(HP* php, HPDataType* a, int n)
{assert(php);php->a = (HPDataType*)malloc(sizeof(HPDataType) * n);if (php->a == NULL){perror("malloc fail");return;}memcpy(php->a, a, sizeof(HPDataType) * n);php->capacity = php->size = n;//HPInitArray:/*初始化堆数组,并将数据拷贝过来有两种方式建堆:向上调整:每个节点都与父节点比较,时间复杂度O(NlogN)向下调整:从最后一个非叶子节点开	始,每个节点与子节点比较,时间复杂度O(N)这里采用向下建堆,复杂度更低*///向上调整,建堆O(N*logN)/*for (int i = 1; i < php->size; i++){AdjustUp(php->a, i);}*///向下建堆,复杂度为O(N)for (int i = (php->size - 1 - 1) / 2; i >= 0; i--){AdjustDown(php->a, php->size,i);}
}void HPDestroy(HP* php)
{assert(php);free(php->a);php->a = NULL;php->capacity = 0;php->size = 0;
}void Swap(int* px, int* py)
{int temp = *px;*px = *py;*py = temp;
}void AdjustUp(HPDataType* a, int child)
{int parent = (child - 1) / 2;while (child > 0){if (a[parent] > a[child]){Swap(&a[parent], &a[child]);child = parent;parent = (parent - 1) / 2;}else{break;}}
}void HPPush(HP* php, HPDataType x)
{assert(php);if (php->size == php->capacity){size_t newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;HPDataType * tmp = realloc(php->a, sizeof(HPDataType) * newCapacity);if (tmp == NULL){perror("realloc fail");return;}php->a = tmp;php->capacity = newCapacity;}php->a[php->size] = x;php->size++;AdjustUp(php->a, php->size - 1);
}HPDataType HPTop(HP* php)
{assert(php);return php->a[0];}void AdjustDown(HPDataType* a, int n, int parent)
{int child = parent * 2 + 1;while (child < n){if (child+1<n && a[child + 1]<a[child]){child++;}if (a[child] < a[parent]){Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}//时间复杂度是:logN
void HPPop(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);
}int HeapSize(HP* php)
{assert(php);return php->size;
}bool HPEmpty(HP* php)
{assert(php);return php->size == 0;
}

🌠堆的实现代码测试

int main()
{int a[] = { 60,70,65,50,32,100 };HP hp;HPInitArray(&hp, a, sizeof(a) / sizeof(int));/*HPInit(&hp);for (int i = 0; i < sizeof(a) / sizeof(int); i++){                         HPPush(&hp, a[i]);}printf("%d\n", HPTop(&hp));HPPop(&hp);printf("%d\n", HPTop(&hp));*/while (!HPEmpty(&hp)){printf("%d\n", HPTop(&hp));HPPop(&hp);}HPDestroy(&hp);return 0;
}

在这里插入图片描述


🚩总结

感谢你的收看,如果文章有错误,可以指出,我不胜感激,让我们一起学习交流,如果文章可以给你一个小小帮助,可以给博主点一个小小的赞😘

请添加图片描述

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

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

相关文章

CSS3病毒病原体图形特效

CSS3病毒病原体图形特效&#xff0c;源码由HTMLCSSJS组成&#xff0c;双击html文件可以本地运行效果&#xff0c;也可以上传到服务器里面 下载地址 CSS3病毒病原体图形特效代码

Java 使用 EasyExcel 实现导入导出(新手篇教程)

官网镇楼↓&#xff0c;觉得我写的不好的同学可以去官网看哦 EasyExcel Maven <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.3.3</version> </dependency> Excel 导入 示例&…

Keil笔记(缘更)

Keil 一、使用Keil时可能会出现的问题1.Project框不见了2.添加文件时找不到3.交换文件位置4.main.c测试报1 warning 二、STLINK点灯操作1.配置寄存器进行点灯2.使用库函数进行点灯 3.GPIO1.LED闪烁 一、使用Keil时可能会出现的问题 1.Project框不见了 view->Project Windo…

数据库不应放在容器中?- B站Kubernetes有状态服务实践(Elasticsearch/Clickhouse)

本期作者 前言 云原生时代下&#xff0c; Kubernetes已成为容器技术的事实标准&#xff0c; 使得基础设施领域应用下自动化运维管理与编排成为可能。对于无状态服务而言&#xff0c; 业界早已落地数套成熟且较完美的解决方案。可对于有状态的服务&#xff0c; 方案的复杂度就以…

SCUI Admin:快速构建企业级中后台前端的利器 让前端开发更快乐。

欢迎加入我们的前端组件学习交流群&#xff0c;可添加群主微信&#xff0c;审核通过后入群。 随着Web技术的不断发展&#xff0c;中后台前端解决方案在各类企业级应用中扮演着越来越重要的角色。SCUI Admin正是一款基于Vue3和elementPlus的WebUI前端框架&#xff0c;旨在帮助开…

Ubuntu查看ros版本-linux查看ros版本

使用ros带的rosversion命令即可查看自己的ros版本&#xff1a; rosversion -d

【四 (4)数据可视化之 Ploty Express常用图表及代码实现 】

目录 文章导航一、介绍二、安装Plotly Express三、导入Plotly Express四、占比类图表1、饼图2、环形图3、堆叠条形图4、百分比堆叠条形图 五、比较排序类1、条形图2、漏斗图3、面积漏斗图 六、趋势类图表1、折线图2、多图例折线图3、分列折线图4、面积图5、多图例面积图 七、频…

mysql实战开发之 mysql 删除一张表某个字段的sql语句

有一张表, 我需要删除这张表其中的某一个或者某几个字段, 相信大家在日常开发中应该会遇到这种情况, 然后刚好自己接触的项目安装的mysql关闭了允许远程连接的设置, 也就是说不允许使用类似于navicat 等可视化工具连接, 那么就没办法通过可视化工具直接去通过鼠标操作就可以 完…

B3620 x 进制转 10 进制(详解)

题目 思路 十进制数13怎么转为二进制数。许多人都知道用13一直除以2&#xff0c;取余数&#xff0c;最后反向将余数组合起来。 首先它是转二进制&#xff0c;我们都知道二进制数是逢二进一&#xff0c;所以我们就看看13有多少个2是吧&#xff0c;于是就用132得到6余1的结果&am…

ASP.NET-Server.HtmlEncode

目录 背景: 1.转义特殊字符&#xff1a; 2.防止跨站脚本攻击&#xff08;XSS&#xff09;&#xff1a; 3.确保输出安全性&#xff1a; 4.保留原始文本形式&#xff1a; 5.与用户输入交互安全&#xff1a; 实例说明: 不用Server.HtmlEncode 效果展示: 用Server.HtmlEnc…

腾讯在线文档下载文档html格式

腾讯在线文档下载文档html格式 步骤 chrome 浏览器打开该文档&#xff08;edge不行&#xff09; 同时按住ctrlp快捷键调出腾讯文档内置的打印页面&#xff0c;打印范围要选择整个工作薄&#xff0c;纸张建议调大一点&#xff0c;边距建议较窄&#xff0c;缩放要选择宽度撑满&…

vue使用elementPlus ui框架,如何给Dialog 对话框添加Loading 自定义类名显示隐藏

vue使用elementPlus ui框架时&#xff0c;如何给Dialog 对话框添加Loading 自定义类名&#xff0c;想要实现dialog对话框区域有loading效果 官方给出的这个API配置项customClass&#xff0c;使用不太明确。暂时无法实现绑定class。 最后的实现方式&#xff1a; <template&…