树和二叉树的基本概念和堆的实现

树的概念及结构

树的概念

树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的
在这里插入图片描述

1.有一个特殊的结点,称为根结点,根节点没有前驱结点
2.除根节点外,其余结点被分成M(M>0)个互不相交的集合T1、T2、……、Tm,其中每一个集合Ti(1<= i <= m)又是一棵结构与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个或多个后继
3.因此,树是递归定义的

这里大家要注意到一个点:树形结构中,子树之间不能有交集,否则就不是树形结构
例如:
以下几种情况都不是树
在这里插入图片描述

树的相关概念

在这里插入图片描述
上图是一棵树,我们用他来了解相关的定义:
节点的度:一个节点含有的子树的个数称为该节点的度; 如上图: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)棵互不相交的树的集合称为森林

树的表示

树结构相对线性表就比较复杂了,要存储表示起来就比较麻烦了,既然保存值域,也要保存结点和结点之间的关系,实际中树有很多种表示方式如:双亲表示法,孩子表示法、孩子双亲表示法以及孩子兄弟表示法等。我们这里就简单的了解其中最常用的孩子兄弟表示法

typedef int DataType;
struct Node
{struct Node* _firstChild1; // 第一个孩子结点struct Node* _pNextBrother; // 指向其下一个兄弟结点DataType _data; // 结点中的数据域
};

孩子兄弟表示法就是一个节点带着一个数据,一个孩子,一个兄弟
孩子指向下一层的第一个孩子节点,兄弟节点则指向右侧的同一层的兄弟节点
在这里插入图片描述

二叉树的概念及结构

二叉树的概念

一棵二叉树是结点的一个有限集合,该集合:

  1. 或者为空
  2. 由一个根节点加上两棵别称为左子树和右子树的二叉树组成
    在这里插入图片描述
    你会发现二叉树的规则:
    二叉树不存在度大于2的结点
    二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树

但是二叉树也可以是空树或者只有一个节点
在这里插入图片描述

特殊的二叉树

  1. 满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是 ,则它就是满二叉树。
  2. 完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树
    在这里插入图片描述
    满二叉树一定是完全二叉树!

二叉树的性质

  1. 若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有2^(i-1) 个结点.
  2. 若规定根节点的层数为1,则深度为h的二叉树的最大结点数是( 2^h)-1
  3. 对任何一棵二叉树, 如果度为0其叶结点个数为 n0, 度为2的分支结点个数为 n2,则有 n0 = n2 +1
  4. 若规定根节点的层数为1,具有n个结点的满二叉树的深度,h= log2(n+1) (ps: 是log以2为底,n+1为对数)

二叉树的存储结构

二叉树一般可以使用两种结构存储,一种顺序结构,一种链式结构

顺序存储

顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费。而现实中使用中只有堆才会使用数组来存储。二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。
在这里插入图片描述
链式存储过于复杂,这里不做过多的讲解

堆的实现

这里我们用顺序结构来实现,和堆一起

堆的概念及结构

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

其节点总是大于父节点的值就是小堆
节点的值总是小于父节点的值就是大堆
在这里插入图片描述
现在我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆。向下调整算法有一个前提:左右子树必须是一个堆,才能调整
在这里插入图片描述
下面我们给出一个数组,这个数组逻辑上可以看做一颗完全二叉树,但是还不是一个堆,现在我们通过算法,把它构建成一个堆。根节点左右子树不是堆,我们怎么调整呢?这里我们从倒数的第一个非叶子节点的子树开始调整,一直调整到根节点的树,就可以调整成堆。
在这里插入图片描述

向下调整算法

我们用父节点开始调整,如果当父节点的值小于子节点的值时我们就将其交换(此处的算法时调整为小堆)
代码实现如下:

void Swap(HPDataType* a, HPDataType* b)
{HPDataType temp = *a;*a = *b;*b = temp;
}
void Adjustdown(HPDataType* a, int n, int parent)
{int child = parent * 2 + 1;;while (child < n){if ((child + 1) < n && a[child] < a[child + 1]){child++;}if (a[child] > a[parent]){Swap(&a[parent], &a[child]);parent = child;;child = parent * 2 + 1;}else{break;}}
}
堆的初始化

初始化小菜一碟了

void HeapInit(Heap* hp)
{assert(hp);hp->a = NULL;hp->capacity = 0;hp->size = 0;
}
堆的销毁

销毁也是轻车熟路了

void HeapDestory(Heap* hp)
{assert(hp);free(hp->a);hp->a = NULL;hp->capacity = 0;hp->size = 0;
}
判断堆是否为空

直接看size是否为0即可

int HeapEmpty(Heap* hp)
{assert(hp);return hp->size == 0;
}
返回堆顶元素

直接返回数组首元素(记得判断是否为就空)

HPDataType HeapTop(Heap* hp)
{assert(hp);assert(!HeapEmpty(hp));return hp->a[0];
}
堆增加元素

增加元素前我们首先判断数组是否已满,看是否需要扩容
然后将数组size位置赋值,同时size++,然后再用向下(上)调整算法

void HeapPush(Heap* hp, HPDataType x)
{assert(hp);if (hp->size == hp->capacity){int newcapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;HPDataType* temp = (HPDataType*)realloc(hp->a, sizeof(HPDataType) * newcapacity);if (temp == NULL){perror("realloc");return;}hp->a = temp;hp->capacity = newcapacity;}hp->a[hp->size] = x;hp->size++;Adjustup(hp->a, hp->size - 1);
}
堆删除元素(堆顶)

栈的删除元素不能直接删除,因为因为直接删除头部元素后,父子关系全乱了,还需要重新建堆,所以我们首先进行首位交换,然后删除掉后面的的元素,再调整堆

void HeapPop(Heap* hp)
{assert(hp);assert(!HeapEmpty(hp));Swap(&hp->a[0], &hp->a[hp->size - 1]);//先交换首尾hp->size--;//再删除Adjustdown(hp->a, hp->size, 0);//再重新排栈
}

堆的实现完整代码如下:

void HeapInit(Heap* hp)
{assert(hp);hp->a = NULL;hp->capacity = 0;hp->size = 0;
}void HeapDestory(Heap* hp)
{assert(hp);free(hp->a);hp->a = NULL;hp->capacity = 0;hp->size = 0;
}int HeapEmpty(Heap* hp)
{assert(hp);return hp->size == 0;
}HPDataType HeapTop(Heap* hp)
{assert(hp);assert(!HeapEmpty(hp));return hp->a[0];
}void Swap(HPDataType* a, HPDataType* b)
{HPDataType temp = *a;*a = *b;*b = temp;
}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 = (child - 1) / 2;}else{break;}}
}void Adjustdown(HPDataType* a, int n, int parent)
{int child = parent * 2 + 1;;while (child < n){if ((child + 1) < n && a[child] < a[child + 1]){child++;}if (a[child] > a[parent]){Swap(&a[parent], &a[child]);parent = child;;child = parent * 2 + 1;}else{break;}}
}void HeapPush(Heap* hp, HPDataType x)
{assert(hp);if (hp->size == hp->capacity){int newcapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;HPDataType* temp = (HPDataType*)realloc(hp->a, sizeof(HPDataType) * newcapacity);if (temp == NULL){perror("realloc");return;}hp->a = temp;hp->capacity = newcapacity;}hp->a[hp->size] = x;hp->size++;Adjustup(hp->a, hp->size - 1);
}void HeapPop(Heap* hp)
{assert(hp);assert(!HeapEmpty(hp));Swap(&hp->a[0], &hp->a[hp->size - 1]);//先交换首尾hp->size--;//再删除Adjustdown(hp->a, hp->size, 0);//再重新排栈
}

好了,本篇博客到此结束,谢谢大家的支持!

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

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

相关文章

04-配置远程仓库的SSH免密登陆

配置SSH免密登录 配置步骤 创建好的远程仓库也可以使用SSH的方式进行访问,但如果没有配置公钥会有警告 第一步: 删除用户家目录下的.ssh目录,如果没有该目录或者该目录下已经有密钥了就不用执行该操作 #进入当前用户的家目录,删除.ssh 目录 LayneLAPTOP-Layne MINGW64 ~ $ r…

python datetime 获取特定一天的后一天或者后几天

这里写自定义目录标题 1 获取特定天的时间对象 具体时间格式参考&#xff1a;Python time strptime()和strftime()-CSDN博客 import datetimetimer datetime.datetime.strptime(date, "%Y-%m-%d")2 获取下一天或者【下x天】的数据并进行格式转换 # 下一天数据 ne…

基于SpringBoot+Vue的前后端分离的房屋租赁系统2

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目背景介绍&#xff1a; 开发过程中&#xff0…

uniApp打包的手机app如果用户没开启通知权限、引导用户开启

封装一个setPermissions.js文件 /*** 如果用户没开启通知权限、引导用户开启 */ export function setPermissions() {// #ifdef APP-PLUS if (plus.os.name Android) {var main plus.android.runtimeMainActivity();var pkName main.getPackageName();var uid main.getApp…

基于WebSocket实现客户聊天室

目录 一、实现聊天室原理 二、聊天室前端代码 三、聊天室后端代码&#xff08;重点&#xff09; 四、聊天室实现效果展示 一、实现聊天室原理 1.1 介绍websocket协议 websocket是一种通信协议&#xff0c;再通过websocket实现弹幕聊天室时候&#xff0c;实现原理是客户端首…

【动态规划】LeetCode-931.下降路径最小和

&#x1f388;算法那些事专栏说明&#xff1a;这是一个记录刷题日常的专栏&#xff0c;每个文章标题前都会写明这道题使用的算法。专栏每日计划至少更新1道题目&#xff0c;在这立下Flag&#x1f6a9; &#x1f3e0;个人主页&#xff1a;Jammingpro &#x1f4d5;专栏链接&…

服务器基础知识

服务器被誉为互联网之魂。我第一次见到服务器是在学校图书馆&#xff0c;是一种机架式服务器&#xff0c;第二次见到服务器是在公司机房。本期文章是对服务器进行大盘点和梳理&#xff0c;会介绍我拆装服务器过程&#xff0c;从中的学习感悟。 图片来自 Pexels 01 服务器 服务…

VSCode 开发C/C++实用插件分享——codegeex

VSCode 开发C/C实用插件分享——codegeex 一、codegeex 一、codegeex CodeGeeX 智能编程助手是一款编程插件&#xff0c;CodeGeeX支持多种主流IDE&#xff0c;如VS Code、IntelliJ IDEA、PyCharm、Vim等&#xff0c;同时&#xff0c;支持Python、Java、C/C、JavaScript、Go等多…

图片点击放大

在列表中添加插槽 <template slot-scope"scope">&#xff0c;获取当前点击的数据 在图片中添加点击事件的方法&#xff0c;用来弹出窗口 <vxe-columnfield"icon"title"等级图标"><template slot-scope"scope"><…

PyLMKit(3):基于角色扮演的应用案例

角色扮演应用案例RolePlay 0.项目信息 日期&#xff1a; 2023-12-2作者&#xff1a;小知课题: 通过设置角色模板并结合在线搜索、记忆和知识库功能&#xff0c;实现典型的对话应用功能。这个功能是大模型应用的基础功能&#xff0c;在后续其它RAG等功能中都会用到这个功能。功…

使用MD5当做文件的唯一标识,这样安全么?

使用MD5作为文件唯一标识符可靠么&#xff1f; 文章目录 使用MD5作为文件唯一标识符可靠么&#xff1f;什么是MD5&#xff1f;MD5的用途MD5作为文件唯一标识的优劣优势劣势 使用MD5作为文件唯一标识的建议其他文件标识算法结束语 什么是MD5&#xff1f; MD5&#xff08;Messag…

postman接口测试教程与实例分享

postman 的界面图 各个功能区的使用如下&#xff1a; 快捷区&#xff1a; 快捷区提供常用的操作入口&#xff0c;包括运行收藏夹的一组测试数据&#xff0c;导入别人共享的收藏夹测试数据&#xff08;Import from file, Import from folder, Import from link等&#xff09;&…

zookeeper心跳检测 (实操课程)

本系列是zookeeper相关的实操课程&#xff0c;课程测试环环相扣&#xff0c;请按照顺序阅读来学习和测试zookeeper。 阅读本文之前&#xff0c;请先阅读----​​​​​​zookeeper 单机伪集群搭建简单记录&#xff08;实操课程系列&#xff09;zookeeper 客户端常用命令简单记录…

kubernetes中YAML介绍;API资源对象Pod;Pod原理和生命周期;Pod资源限制

YAML介绍&#xff1b;API资源对象Pod&#xff1b;Pod原理和生命周期&#xff1b;Pod资源限制 1&#xff09;认识YAML 官网&#xff08;https://yaml.org/&#xff09; YAML 语言创建于 2001 年&#xff0c;比 XML 晚了三年。YAML虽然在名字上模仿了XML&#xff0c;但实质上与…

【【FPGA 之 MicroBlaze 自定义IP核 之 呼吸灯实验】】

FPGA 之 MicroBlaze 自定义IP核 之 呼吸灯实验 通过创建和封装 IP 向导的方式来自定义 IP 核&#xff0c;支持将当前工程、工程中的模块或者指定文件目录封装成 IP 核&#xff0c;当然也可以创建一个带有 AXI4 接口的 IP 核&#xff0c;用于 MicroBlaze 软核处理器和可编程逻辑…

Day59权限提升-win溢出漏洞ATSCps提权

针对Windows系统个人主流操作系统是win7/8/10等等&#xff0c;针对服务器就win2003和2008比较多&#xff0c; 明确权限提升问题&#xff0c;web和本地&#xff1a; 举个例子&#xff0c;现在获得了一个网站权限&#xff0c;这个权限只可以对网站自身的东西进行操作&#xff0…

Python字典类型

目录 目标 版本 官方文档 简介 实战 创建 循环 常用方法 目标 掌握字典类型的使用方法&#xff0c;包括&#xff1a;创建、循环、常用方法等操作。 版本 Python 3.12.0 官方文档 Mapping Types — dicthttps://docs.python.org/3/library/stdtypes.html#mapping-type…

Halcon参考手册目标检测和实例分割知识总结

1.1 目标检测原理介 目标检测&#xff1a;我们希望找到图像中的不同实例并将它们分配给某一个类别。实例可以部分重叠&#xff0c;但仍然可以区分为不同的实例。如图(1)所示&#xff0c;在输入图像中找到三个实例并将其分配给某一个类别。 图(1)目标检测示例 实例分割是目标检…

敌方移动发射[java坦克大战]

1.首先要确保判断如果敌人坦克存活并且敌人子弹集合等于0了&#xff0c;就根据坦克方向创建一颗子弹&#xff0c;放入到shots集合&#xff0c;并启动。(逻辑&#xff1a;发射子弹后&#xff0c;敌人坦克向前移动一段距离后转向&#xff0c;敌人坦克仍然存活并且刚发射的子弹消亡…

C++-设计一个特殊类

目录 一.设计一个类&#xff0c;不能被拷贝 二.设计一个类只能在堆上创建对象 三.设计一个类只能在栈上创建对象 四. 请设计一个类&#xff0c;不能被继承 五.请设计一个类&#xff0c;只能创建一个对象(单例模式) 1.单例模式&#xff1a; 2. 饿汉模式 一.设计一个类&#x…