数据结构——二叉树——堆(1)
今天,我们来写一篇关于数据结构的二叉树的知识。
在学习真正的二叉树之前,我们必不可少的先了解一下二叉树的相关概念。
一:树的概念
树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的

注意:树形结构中,子树之间不能有交集,否则就不是树形结构
除了根结点外,每个结点有且仅有一个父结点
一颗N节点的树又N-1条边
下面我们来具体给出树的相关名词:

节点的度:一个节点含有的子树的个数称为该节点的度; 如上图:A的为6
叶节点或终端节点:度为0的节点称为叶节点; 如上图:B、C、H、I...等节点为叶节点
非终端节点或分支节点:度不为0的节点; 如上图:D、E、F、G...等节点为分支节点
双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点; 如上图:A是B的父节点
孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点; 如上图:B是A的孩子节点
兄弟节点:具有相同父节点的节点互称为兄弟节点; 如上图:B、C是兄弟节点树的度:
一棵树中,最大的节点的度称为树的度; 如上图:树的度为6
节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
树的高度或深度:树中节点的最大层次; 如上图:树的高度为4
堂兄弟节点:双亲在同一层的节点互为堂兄弟;如上图:H、I互为兄弟节点
节点的祖先:从根到该节点所经分支上的所有节点;如上图:A是所有节点的祖先
子孙:以某节点为根的子树中任一节点都称为该节点的子孙。如上图:所有节点都是A的子孙
森林:由m(m>0)棵互不相交的树的集合称为森林
标注:红色的为常用的,橙色为概念
有了上面的铺垫后,我们来认识一下二叉树:
二叉树
图:

二叉树组成:
由三部分:根,左子树(左孩子),右子树(右孩子)

从上面,我们知道:
1.二叉树不存在度大于2的结点
2.二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树
3. 存在情况:
接下来,我们认识
特殊的二叉树:
1. 满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是2^k-1 ,则它就是满二叉树。
2. 完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树
证明:满二叉树中高度为h的有多少个结点?

完全二叉树:前h-1都是满的
最后一层要求从左到右是连续的高度为h的完全二叉数,节点数量的范围是[2^(h-1),2^h-1]
由上面知道满二叉树为2^h-1.
根据完全二叉树的定义
2^(h-1)-1----不算最后一层的。然后再算最后一层:2^(h-1)-1+1==2^(h-1)
所以,它的范围:[2^(h-1),2^h-1]。
此外,对任何一颗二叉树,如果度为0的叶结点个数为n0,度为2的分支节点为2 ,则由n0=n2+1.
结论:度为0的永远比度为2的多1。
二叉树的存储结构
二叉树一般可以使用两种结构存储,一种顺序结构,一种链式结构。
顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费。而现实中使用中只有堆才会使用数组来存储,
我们知道:顺序表是都一样的,实际上就是数组嘛
而链表不一样,我们通常用箭头那些是想象出来的,实际上并没有箭头的。
同样,二叉树也是:
逻辑结构 想象出来的
物理结构 实实在在内存中是如何存储的
反向过来,你把这棵树的一层一层的值依次往数组里面存储
如下图:
逻辑结构:(想象成这样)

物理结构:(实际就是数组)
另外,我们平时画图的时候,很麻烦画出第一张图那么标准,其实,我们也是可以画出这样子
简便:

通过观察上面得出的规律:
表示二叉树的值在数组位置中的父子下标关系
parent=(child-1)/2
leftchlid=parent*2+1
rightchlid=parent*2+2
注意,这里必须是从0开始,不然就乱了
有人会问了,能不能在完全二叉树那里使用呢?

这里用数组存储完全二叉数,有局限不适合。
因为浪费很多空间,数组存储表示二叉树只适合完全二叉树。
好了,有了上面的铺垫后,现在让我们来实现一下堆。
堆
概念:
什么是堆呢?
我们将堆分为大堆和小堆
小堆

大堆:

在写堆时,底层代码实际就是数组
注意:堆不是排序,堆只规定父亲的大小,并没有规定它的左右孩子的大小
插入时,可能会影响部分祖先。
实际控制的是数组,写的时候把它想象成树
一:定义结构体
typedef int HPDataType;
typedef struct Heap
{HPDataType* a;int size;int capacity;
}HP;
1.需要一个数组
2.要算出数组的大小
3.最大容量
二:初始化部分
void HeapInit(HP* php)
{assert(php);php->a = (HPDataType*)malloc(sizeof(HPDataType)*4);if (php->a == NULL){perror("malloc fail");return;}php->size = 0;php->capacity = 4;
}
这里初始化跟之前的都差不多。
1.创建数组,用malloc
2.初始化size为0;
3.因为上面创建了数组:4个位置,所以初始化时的最大容量为4。
三:销毁部分
void HeapDestroy(HP* php)
{assert(php);free(php->a);php->a = NULL;php->capacity = php->size = 0;
}
1.置空,置零就可以了。
四:Push部分
首先,我们得思考一下,堆咋push的。
由于本质上是数组,所以我们push在数组的最后那里插。
假如在下面数组push一个数字60.

因此我们会写成这样一个代码:
void HeapPush(HP* php, HPDataType x)
{assert(php);if (php->size == php->capacity){HPDataType* tmp = (HPDataType*)realloc(php->a, sizeof(HPDataType) * php->capacity*2);if (tmp == NULL){perror("realloc fail");return;}php->a = tmp;php->capacity *= 2;}php->a[php->size] = x;php->size++;调整部分下面有讲AdjustUp(php->a, php->size - 1);
}
但是呢,如果是像上面一样push了一个数字的话,堆就乱了:
变成了

面对这种问题,我们又该怎么办呢?
这里我们使用一种叫向上调整法解决这种问题:

我们以大堆来举例子:
我们发现上面是不是将30和60的位置交换了,就可以重新变成大堆了?
那么,我们又是怎么变成这一步的呢?
不能发现,你看
1.将分为左孩子和右孩子和根三部分。
2.比较左孩子和右孩子,拿出大的孩子,再跟根比较。
3.如果大的孩子的数字大于根,就交换。反之不换。
对上述做法叫做向上调整法。
对此我们写一个函数:
向上调整部分
void AdjustUp(HPDataType* a, int child)
{int parent = (child - 1) / 2;//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这个位置,前面数据构成堆
1.我们是不是得找到父亲结点。上面我们已经给出了父亲与孩子之间的公式变换了
2.由于交换这个代码内容在后面也是常用到的,所以我们单独封装一个函数
交换部分
void Swap(HPDataType* p1, HPDataType* p2)
{HPDataType x = *p1;*p1 = *p2;*p2 = x;
}
3.这样容易忽略的问题是:
while的条件:弄成 while(child > 0) 而不要弄成while (parent >= 0)
这里看似都没有问题运行时,但是会不好。
parent >= 0,意味着parent<0才会中止,但是,parent会小于0吗?不会,因为这里最差的情况就是孩子等于0.
parent = (child - 1) / 2;即就是-1/2,按理是0.5,但是这里是整形,所以还是0,还是会进入循环。
但是呢?这个程序不会死循环,parent=0时,进入循环,但是它不满足if (a[child] > a[parent])条件,所以还是会到达break。
所以能正常跑,但是不好,最好用child>0.
删除部分
void HeapPop(HP* php)
{assert(php);后面讲到assert(!HeapEmpty(php));// 删除数据Swap(&php->a[0], &php->a[php->size - 1]);php->size--;AdjustDown(php->a, php->size, 0);
}
堆的删除部分,有人可能会想直接这样删

但是,接下来的堆就会变成这样:

直接挪动删除 :
1.效率低下。
2.父子兄弟关系全乱了。
那么,我们想到用间接的方法来解决这种问题:

1.我们先把第一个和最后一个交换。
2.删除最后一个。
3.接着向下调整法。
1)什么是向下调整法,即从上面往下调
1.找到孩子中大的数字,与父亲比较,孩子大的就交换。反之不交换
向下调整部分
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;}}
}
1.这里采用的是假设左孩子大,然后再循环里面再弄个if语句,如果右孩子大的话,就交换变成右孩子。(因为左右孩子再邻位,相差1)
2.接着再比较父亲结点,与大的孩子,大就交换,再更新父亲结点和孩子结点。反之就break,跳出循环。
返回顶位置
HPDataType HeapTop(HP* php)
{assert(php);return php->a[0];
}
判断空
bool HeapEmpty(HP* php)
{assert(php);return php->size == 0;
}
返回大小
int HeapSize(HP* php)
{assert(php);return php->size;
}
好了,最后
附上总代码
Heap.h部分
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>typedef int HPTypeData;
typedef struct Heap
{HPTypeData* a;int size;int capacity;}Heap;void HPInit(Heap* php);
void HPDestory(Heap* php);
void HPPush(Heap* php, HPTypeData x);
void HPPop(Heap* php);
HPTypeData HPtop(Heap* php);
bool HPEmpty(Heap* php);
int HPSize(Heap* php);
Heap.c部分
#define _CRT_SECURE_NO_WARNINGS 1
#include "Heap.h"void HPInit(Heap* php)
{assert(php);php->a =(HPTypeData*)malloc(sizeof(HPTypeData)*4);if (php->a == NULL){perror("malloc fail");return;}php->capacity = 4;php->size = 0;
}
void HPDestory(Heap* php)
{assert(php);free(php->a);php->a = NULL;php->size = 0;php->capacity = 0;
}void Swap(HPTypeData* p1,HPTypeData* p2)
{HPTypeData temp = *p1;*p1 = *p2;*p2 = temp;
}
void Adjustup(HPTypeData* a, int child)
{int parent = (child - 1) / 2;while (child > 0){if (a[child] > a[parent]){Swap(&a[child], &a[parent]);child = parent;parent= (child - 1) / 2;}else{break;}}
}
void HPPush(Heap* php, HPTypeData x)
{assert(php);if (php->a == php->capacity){HPTypeData* temp = (HPTypeData*)realloc(php->a, sizeof(HPTypeData) * php->capacity * 2);if (temp == NULL){perror("realloc fail");return;}php->a = temp;php->capacity *= 2;}php->a[php->size] = x;php->size++;//Adjustup(php->a,php->a[php->size-1]);Adjustup(php->a,php->size-1);}Adjustdown(HPTypeData* 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[parent],&a[child]);parent = child;child= parent * 2 + 1;}else{break;}}
}
void HPPop(Heap* php)
{assert(php);assert(!HPEmpty(php));Swap(&php->a[0], &php->a[php->size - 1]);php->size--;Adjustdown(php->a, php->size, 0);
}
HPTypeData HPtop(Heap* php)
{assert(php);return php->a[0];
}
bool HPEmpty(Heap* php)
{assert(php);return php->size == 0;
}
int HPSize(Heap* php)
{assert(php);return php->size;
}
test.c部分
#define _CRT_SECURE_NO_WARNINGS 1
#include "Heap.h"//int main()
//{
// HP hp;
// HeapInit(&hp);
// HeapPush(&hp, 4);
// HeapPush(&hp, 18);
// HeapPush(&hp, 42);
// HeapPush(&hp, 12);
// HeapPush(&hp, 21);
// HeapPush(&hp, 3);
// HeapPush(&hp, 5);
// HeapPush(&hp, 5);
// HeapPush(&hp, 50);
// HeapPush(&hp, 5);
// HeapPush(&hp, 5);
// HeapPush(&hp, 15);
// HeapPush(&hp, 5);
// HeapPush(&hp, 45);
// HeapPush(&hp, 5);
//
// int k = 0;
// scanf("%d", &k);
// while (!HeapEmpty(&hp) && k--)
// {
// printf("%d ", HeapTop(&hp));
// HeapPop(&hp);
// }
// printf("\n");
//
// return 0;
//}// 排升序 -- 建大堆
void HeapSort(int* a, int n)
{// 建堆 -- 向上调整建堆for (int i = 1; i < n; ++i){AdjustUp(a, i);}// 自己先实现}int main()
{int a[10] = { 2, 1, 5, 7, 6, 8, 0, 9, 4, 3}; // 对数组排序HeapSort(a, 10);return 0;
}
最后,到了本次鸡汤部分:
小小的人,有大大的梦想!干!
相关文章:
数据结构——二叉树——堆(1)
今天,我们来写一篇关于数据结构的二叉树的知识。 在学习真正的二叉树之前,我们必不可少的先了解一下二叉树的相关概念。 一:树的概念 树是一种非线性的数据结构,它是由n(n>0)个有限结点组成一个具有层…...
window保存好看的桌面壁纸
1、按下【WINR】快捷键调出“运行”窗口,输入以下命令后回车。 %localappdata%\Packages\Microsoft.Windows.ContentDeliveryManager_cw5n1h2txyewy\LocalState\Assets 2、依次点击【查看】【显示】,勾选【隐藏的项目】,然后按【CtrlA】全部…...
docker安装Redis:docker离线安装Redis、docker在线安装Redis、Redis镜像下载、Redis配置、Redis命令
一、镜像下载 1、在线下载 在一台能连外网的linux上执行docker镜像拉取命令 docker pull redis:7.4.0 2、离线包下载 两种方式: 方式一: -)在一台能连外网的linux上安装docker执行第一步的命令下载镜像 -)导出 # 导出镜像…...
98.1 AI量化开发:长文本AI金融智能体(Qwen-Long)对金融研报大批量处理与智能分析的实战应用
目录 0. 承前1. 简介1.1 通义千问(Qwen-Long)的长文本处理能力 2. 基础功能实现2.1 文件上传2.2 单文件分析2.3 多文件分析 3. 汇总代码&运行3.1 封装的工具函数3.2 主要功能特点3.3 使用示例3.4 首次运行3.5 运行结果展示 4. 注意事项4.1 文件要求4.2 错误处理机制4.3 最佳…...
【自然语言处理(NLP)】长短期记忆网络(Long - Short Term Memory,LSTM)原理和代码实现(从零实现、Pytorch实现)
文章目录 介绍长短期记忆网络(Long - Short Term Memory,LSTM)结构原理候选记忆元符号含义公式含义 记忆元符号含义公式含义 隐状态符号含义公式含义 特点应用实现 LSTMpytorch实现 个人主页:道友老李 欢迎加入社区:道…...
八股学习 微服务篇
微服务篇 常见面试内容Spring Cloud 常见组件注册中心Ribbon负载均衡策略服务雪崩 常见面试内容 Spring Cloud 常见组件 Spring Cloud有5个常见组件: Eureka/Nacos:注册中心;Ribbon:负载均衡;Feign:远程调用;Hystrix/Sentinel:服…...
TCP协议:互联网数据传输的守护者
在互联网的浩瀚海洋中,数据如同涓涓细流,无时无刻不在流动。而这些数据的稳定、可靠传输,离不开一个重要的协议——TCP(Transmission Control Protocol,传输控制协议)。TCP协议作为互联网协议族中的核心成员…...
协助工具-任意门导航
任意门导航网址:随意门导航-最全的实用导航网站,好用简洁宝藏网址神器...
【MCAL实战】MCU模块配置实践
目录 前言 正文 1.硬件分析 1.1 MCU系统模式分析 1.2MCU晶振使用分析 2.MCU通用配置 2.1 McuGeneralConfiguration 2.2 McuModuleConfiguration 2.3 McuResetSettingConf 2.4 McuTrapSettingConf 2.4 其他 3.MCU模式配置 3.1 McuModeSettingConf_0 3.2 McuModeSe…...
OpenAI 发布首个 AI 智能体
OpenAI 发布首个 AI 智能体 当地时间 1 月 23 日,OpenAI 发布了首个 AI 智能体 Operator124。以下是关于它的详细介绍2: 功能用途 操作网页:可模拟人类操作网页浏览器,能进行点击、滚动、输入等操作,例如在 OpenTable…...
【Python】导入类
随着不断给类添加功能,文件可能变得很长,即便妥善地使用了继承亦如此。为遵循Python的总体理念,应让文件尽可能整洁。 Python在这方面提供了帮助,允许将类存储在模块中,然后在主程序中导入所需的模块。 导入单个类 下…...
Deepseek实现本地电影文件批量重命名为infuse格式,可匹配IMDB
import os from openai import OpenAI# 初始化DeepSeek客户端 client OpenAI(api_key"<DeepSeek API Key>", base_url"https://api.deepseek.com")def parse_filename_with_deepseek(filename):"""使用DeepSeek API解析文件名并生成…...
Nginx部署的前端项目刷新404问题
1,查看问题 我部署的81端口是监听tlias项目的,我直接访问端口页面可以出现内容。 我在浏览器舒服端口之后回车,会重定向页面。但是我在重定向之后的页面刷新浏览器就会出现404的问题。 下面是刷新浏览器后的效果 2,在nginx.cnf …...
Boot 系统选择U启动
1.进入Boot 系统 F2 或 Del Boot --->Boot 0ption Priorities #4 KingstwongDataTravele 是U盘 调整搭到#1 2.保持重启就好...
XSLT 编辑 XML:深度解析与实际应用
XSLT 编辑 XML:深度解析与实际应用 引言 XML(可扩展标记语言)和XSLT(可扩展样式表语言转换)是处理和转换XML数据的重要工具。本文将深入探讨XSLT在编辑XML文档中的应用,包括其基本概念、语法结构、以及实…...
项目文章 | PNAS 斑马鱼转录因子ChIP-seq助力解析GATA6突变相关的肝脏疾病机制
近日,西南大学阮华/黄红辉团队联合重庆大学邱菊辉/王贵学团队在PNAS发表了题为“An animal model recapitulates human hepatic diseases associated with GATA6 mutations”的研究论文。该研究构建了一个gata6敲除斑马鱼模型,它重现了gata6突变患者的大…...
easyexcel-导入(读取)(read)-示例及核心部件
文章目录 导入(读取)(read)-示例及核心部件导入(读取)(read)-核心部件EasyExcel(EasyExcelFactory) # 入口read() # read()方法用于构建workbook(工作簿)对象,new ExcelReaderBuilder()doReadAll()这里选XlsxSaxAnalyser这个实现类吧然后到这个类XlsxRowHandler&…...
作业day3
请使用dup2 fgets printf 实现文件拷贝功能、 文件1: 复后文件: #define BUFFER_SIZE 1024 void file_copy(const char* src_file, const char* dest_file) { int src_fd, dest_fd; char buffer[BUFFER_SIZE]; // 打开源文件 src_fd open(s…...
第五节 MATLAB命令
本节的内容将提供常用的一些MATLAB命令。 在之前的篇章中我们已经知道了MATLAB数值计算和数据可视化是一个交互式程序,在它的命令窗口中您可以在MATLAB提示符“>>”下键入命令。 MATLAB管理会话的命令 MATLAB提供管理会话的各种命令。如下表所示:…...
Oracle 普通用户连接hang住处理方法
一、现象说明 $ sqlplus / as sysdbaSQL*Plus: Release 19.0.0.0.0 - Production on Wed Dec 18 16:49:19 2024 Version 19.11.0.0.0Copyright (c) 1982, 2020, Oracle. All rights reserved.Connected to: Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Pro…...
Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...
阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...
HTML 列表、表格、表单
1 列表标签 作用:布局内容排列整齐的区域 列表分类:无序列表、有序列表、定义列表。 例如: 1.1 无序列表 标签:ul 嵌套 li,ul是无序列表,li是列表条目。 注意事项: ul 标签里面只能包裹 li…...
五年级数学知识边界总结思考-下册
目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...
linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
大数据学习(132)-HIve数据分析
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言Ǵ…...
基于SpringBoot在线拍卖系统的设计和实现
摘 要 随着社会的发展,社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统,主要的模块包括管理员;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...
