初识--树(1)
下面就是这篇博客要讲的内容
- 树
- 二叉树
- 堆
- 树概念及结构
- 二叉树的概念及结构
- 二叉树的实现
- 堆的概念及运用
这篇博客主要以二叉树为主要内容。
1、树的概念及结构
1.1树的概念:
树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。
其中有三个注意的点:
(1)树有一个特殊的结点,称为根结点,根结点没有前驱结点
(2)除根结点外,其余结点被分成M(M>0)个互不相交的集合T1、T2、……、Tm,其中每一个集合Ti(1<= i<= m)又是一棵结构与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个或多个后继因此,树是递归定义的。
(3)树形结构中,子树之间不能有交集,否则就不是树形结构


在第一个图中A节点就是我们的根节点。
1.2树的基本概念

(1)结点的度:一个结点含有的子树的个数称为该结点的度; 如上图:A的为6
(2)叶结点或终端结点:度为0的结点称为叶结点; 如上图:B、C、H、I…等结点为叶结点
(3)非终端结点或分支结点:度不为0的结点; 如上图:D、E、F、G…等结点为分支结点
(4)双亲结点或父结点:若一个结点含有子结点,则这个结点称为其子结点的父结点; 如上图:A是B的父结点
(5)孩子结点或子结点:一个结点含有的子树的根结点称为该结点的子结点; 如上图:B是A的孩子结点
(6)兄弟结点:具有相同父结点的结点互称为兄弟结点; 如上图:B、C是兄弟结点
(7)树的度:一棵树中,最大的结点的度称为树的度; 如上图:树的度为6
(8)结点的层次:从根开始定义起,根为第1层,根的子结点为第2层,以此类推;
(9)树的高度或深度:树中结点的最大层次; 如上图:树的高度为4
(10)堂兄弟结点:双亲在同一层的结点互为堂兄弟;如上图:H、I互为兄弟结点
(11)结点的祖先:从根到该结点所经分支上的所有结点;如上图:A是所有结点的祖先
(12)子孙:以某结点为根的子树中任一结点都称为该结点的子孙。如上图:所有结点都是A的子孙
森林:由m(m>0)棵互不相交的树的集合称为森林;
1.3树的表示
这里就简单的了解其中最常用的孩子兄弟表示法——左孩子右兄弟。
typedef int DataType;
struct Node
{struct Node* firstChild1;// 第一个孩子结点struct Node* pNextBrother;// 指向其下一个兄弟结点DataType data;// 结点中的数据域
};
这就是我们树中的一个节点。

表现形式如上图。
这种方式来表示树非常的牛!大家可以试想如果我们不用这种方式我们怎样来表示我们的树——
树的分支数我们知道吗?那我们怎样定义我们的分支呢?那就只有顺序表,那顺序表开多大的空间?那样会非常的麻烦,而且会浪费我们的空间,而上图的方式完美解决了我们的问题。
2、二叉树的概念及结构
2.1 二叉树的概念

形如上图每个节点都最多只有两个分支的数就为我们的二叉树。
2.2 特殊的二叉树
- 满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。
- 完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。

2.3 二叉树的性质(非常重要,尽量自己尝试着去推导)
1、若规定根结点的层数为1,则一棵非空二叉树的第i层上最多有(2^(i-1))个结点。
2、若规定根结点的层数为1,则深度为h的二叉树的最大结点数是2^h-1。
3、对任何一棵二叉树, 如果度为0其叶结点个数为n0 , 度为2的分支结点个数为n2 ,则有 n0= n2+1。
4、若规定根结点的层数为1,具有n个结点的满二叉树的深度,h=log(n+1)。(log以2为底)
5、对于具有n个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有结点从0开始编号,则对于序号为i的结点有:
- 若i>0,i位置结点的双亲序号:(i-1)/2;i=0,i为根结点编号,无双亲结点
- 若2i+1<n,左孩子序号:2i+1,2i+1>=n否则无左孩子
- 若2i+2<n,右孩子序号:2i+2,2i+2>=n否则无右孩子
这三个推论在堆的实现、堆排序中使用,所以也希望大家能记住。
由于篇幅原因我这里就不再给大家一步一步推了。
2.4 二叉树的存储
二叉树一般可以使用两种结构存储,一种顺序结构,一种链式结构。
(1)顺序结构
顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费。而现实中使用中只有堆才会使用数组来存储,二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。

顺序结构有什么好处呢?
可以用下标来表示父子关系

(2)链式结构
二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。链式结构又分为二叉链和三叉链,当前我们学习中一般都是二叉链。
typedef int BTDataType;
// 二叉链
struct BinaryTreeNode
{
struct BinTreeNode* left;
// 指向当前结点左孩子
struct BinTreeNode* right; // 指向当前结点右孩子
BTDataType data;
// 当前结点值域
}
这篇博客先了解我们的链式存储,在下一篇我们就介绍顺序存储–堆。
三、二叉树链式结构的实现
3.1 二叉树的遍历(前序、中序以及后序遍历)
学习二叉树结构,最简单的方式就是遍历。所谓二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉树中的结点进行相应的操作,并且每个结点只操作一次。访问结点所做的操作依赖于具体的应用问题。 遍历是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础。

按照规则,二叉树的遍历有:前序/中序/后序的递归结构遍历:
- 前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。
- 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。
- 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。

这里我画出来前序和中序,大家自己来画一下后序遍历,这一块可以为我们后面要学的搜索二叉树打下基础,这一块也是必须要掌握的。
遍历代码部分:
这里我们还没有学会创建我们的树,那么我们就用笨办法,手动去构建我们的树,
(1)、首先我们要先确定树的形状,我这里的是自己随便画的大家可以自己来构建自己想要的树:

(2)代码构建:
typedef struct TreeNode
{int val;struct TreeNode* left;struct TreeNode* right;
}TreeNode;//二叉树的节点TreeNode* creatTree()
{TreeNode* node1 = (TreeNode*)malloc(sizeof(TreeNode));node1->val = 1;node1->left = node1->right = NULL;TreeNode* node2 = (TreeNode*)malloc(sizeof(TreeNode));node2->val = 2;node2->left = node2->right = NULL;TreeNode* node3 = (TreeNode*)malloc(sizeof(TreeNode));node3->val = 3;node3->left = node3->right = NULL;TreeNode* node4 = (TreeNode*)malloc(sizeof(TreeNode));node4->val = 4;node4->left = node4->right = NULL;TreeNode* node5 = (TreeNode*)malloc(sizeof(TreeNode));node5->val = 5;node5->left = node5->right = NULL;TreeNode* node6 = (TreeNode*)malloc(sizeof(TreeNode));node6->val = 6;node6->left = node6->right = NULL;TreeNode* node7 = (TreeNode*)malloc(sizeof(TreeNode));node7->val = 7;node7->left = node7->right = NULL;//创建我们的二叉树节点node1->left = node2; node1->right = node3;node2->left = node4; node2->right = node5;node5->right = node7; node3->right = node6;//链接我们的树return node1;//返回树的根节点
}
(3)、遍历我们的二叉树:
void preOrder(TreeNode* root)
{if (root == NULL)return;printf("%d ", root->val);preOrder(root->left);preOrder(root->right);
}
int main()
{TreeNode* root = creatTree();preOrder(root);//前序遍历return 0;
}
void inOrder(TreeNode* root)//中序遍历
{if (root == NULL)return;inOrder(root->left);printf("%d ", root->val);inOrder(root->right);
}
大家可以看到代码部分非常的简单,但大家一定要明白它是怎样执行的。
3.2 层序遍历
层序遍历:除了先序遍历、中序遍历、后序遍历外,还可以对二叉树进行层序遍历。设二叉树的根结点所在层数为1,层序遍历就是从所在二叉树的根结点出发,首先访问第一层的树根结点,然后从左到右访问第2层上的结点,接着是第三层的结点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。

层序遍历就是一层一层访问,这里只是简单的介绍,这一块我们后面才学。
四、我们学完二叉树要解决的问题
4.1 二叉树的节点个数问题
int treeSize(TreeNode* root)
{if (root == NULL)return 0;return treeSize(root->left) + treeSize(root->right) + 1;
}
关于这块的问题我们采用分置的思想。
节点个数:
1、根为空–0,
2、不为空–左子树+右子树+1。
这一块对于初学者确实非常的有难度,前期我们甚至可以将代码背下来。
大家需要在不断刷题后来慢慢体会。
4.2 二叉树的叶子节点个数问题
int treeLeaveSize(TreeNode* root)
{if (root == NULL)return 0;if (root->left == NULL && root->right == NULL)return 1;return treeLeaveSize(root->left)+ treeLeaveSize(root->right);
}
叶子节点特征:左右子树为空。
叶子:
1、根为空–0,
2、不为空–如果左子树右子树为空,就返回1。
4.3 二叉树的高度问题
int treeHeight(TreeNode* root)
{if (root == NULL)return 0;int left = treeHeight(root->left);int right = treeHeight(root->right);return left > right ? left + 1 : right + 1;
}
高度:
1、根为空–0,
2、不为空–左子树与右子树中大的+1;
当然还有以下问题,大家可以自己尝试去解决,大家可以自己尝试去锻炼自己分置的思想。
1、单值二叉树。
2、 检查两颗树是否相同。
3、另一颗树的子树。
4、对称二叉树。
5、二叉树的构建与销毁。
总的来说这一部分还是非常的有难度,希望大家不要放弃,通过刷题去增强自己的信心。
相关文章:
初识--树(1)
下面就是这篇博客要讲的内容 树 二叉树堆 树概念及结构二叉树的概念及结构二叉树的实现堆的概念及运用 这篇博客主要以二叉树为主要内容。 1、树的概念及结构 1.1树的概念: 树是一种非线性的数据结构,它是由n(n>0)个有限…...
渗透测试实战-菠菜站渗透测试(Nacos反序列化漏洞利用)
免责声明:文章来源于真实渗透测试,已获得授权,且关键信息已经打码处理,请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失,均由使用者本…...
Pytest框架直接右键运行 testcase.py,不执行最外层conftest
随笔记录 目录 1. 背景介绍 2. workaround method 2.1 通过命令行执行 某个测试用例 1. 背景介绍 Pytest 框架结构如下: TestCases:conftest.pyInstanta: conftest.pytest_instanta_tcpdump_pack_len.py# 当直接右键直接 运行 test_instanta_tcpdump_pack_l…...
Cxx primer-chap15-Object-Oriented Programming
面向对象编程的三个基本概念:数据抽象、继承和动态绑定(多态):基类应该提供一些类型无关的成员函数定义,将与类相关的函数留给不同的派生类定义:,派生类是通过类派生列表(class derivation list…...
当黑神话遇上AI:悟空背后的策划逆袭战
声明:此篇为 ai123.cn 原创文章,转载请标明出处链接:https://ai123.cn/2192.html 哈喽,亲爱的游戏迷,随着《黑神话:悟空》的上线,大家都在忙着“直面天命”了吧?今天我想和大家分享最…...
外呼触发通知发送闪信(mod_cti基于FreeSWITCH)
文章目录 前言联系我们手动外呼配置方法例子一:接收到180或183时触发闪信发送例子二:挂断后触发闪信发送 自动外呼配置方法例子:接收到180或183时触发闪信发送 前言 在呼叫中心中间件中,自动外呼触发闪信发送,我们可以…...
8.Java基础概念-方法
欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 Facts speak louder than words! 什么是方法 方法是程序…...
360安全浏览器如何彻底卸载
360安全浏览器是一款广泛使用的网络浏览工具,然而由于各种原因,用户可能需要将其从计算机中彻底移除。下面小编就给大家分享几种彻底卸载360安全浏览器的方法,避免留下影响系统性能的残留信息。(本文由https://chrome.cmrrs.com/站…...
构建基于LLM的应用程序——使用LLM的搜索和推荐引擎
在上一章中,我们介绍了构建对话应用程序的核心步骤。我们从一个基础的聊天机器人开始,然后逐步添加了更复杂的组件,例如记忆、非参数化知识和外部工具。借助LangChain的预构建组件以及Streamlit的UI渲染,这一切都变得相对简单。尽…...
Unity3D 模型碰撞检测问题详解
前言 在Unity3D游戏开发中,模型碰撞检测是至关重要的一环,它负责处理物体之间的交互、触发事件以及物理效果的实现。通过精确的碰撞检测,游戏世界得以呈现出更为真实和动态的交互体验。本文将详细介绍Unity3D中的碰撞检测原理、技术实现以及…...
springcloud集成seata实现分布式事务
Seata 是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。 官网:Apache Seata 文章目录 一、部署1.下载2.修改配置,nacos作注册中心,db存储 二、集成到springcloud项目1.引入依赖2.修改…...
[Leetcode 61][Medium]-旋转链表
目录 一、题目描述 二、整体思路 三、代码 一、题目描述 原题链接 二、整体思路 首先发现这样的规律:当k大于等于链表中节点总数n时,会发现此时旋转后的链表和kk%n时的旋转后的链表一样。同时对于特殊情况n0和n1时,无论k的值为多少都可以…...
高效分页策略:掌握 LIMIT 语句的正确使用方法与最佳实践
本文主要介绍limit 分页的弊端及线上应该怎么用 LIMIT M,N 平时经常见到使用 <limit m,n> 合适的 order by 来实现分页查询,这样做到底性能如何呢? 先来简单分析下,然后再实际验证一下。 无索引条件下,需要做大量的文件排…...
拼图游戏02
文章目录 概要整体架构流程代码过程小结 概要 现在需要将图片添加界面中 关键点在于它如何动态地根据游戏状态更新用户界面。它使用了Swing的布局管理器来定位组件,并且通过ImageIcon和JLabel来显示图像。注意,路径字符串中的反斜杠在Java中是转义字符…...
在本地进行Django支付宝扫码支付-当面付开发
这几天涉及到一个个人项目的支付开发场景,正好完成之后,做一下开发记录,给有需要的朋友做一下参考 涉及安装Python环境请参考我专栏中的历史文章,这里不再重复说明 环境: Python3.11 使用Django框架 因本次代码为沙…...
redis-RedisTemplate.opsForGeo 的geo地理位置相关的方法演示
主要方法:add : 添加一个地理位置distance: 计算两个元素之间的距离hash: 获取元素经纬度坐标经过geohash算法生成的base32编码值position: 获取集合中任意元素的经纬度坐标,可以一次获取多个radius:查询某个坐标或某个成员&#…...
做短视频矩阵要十几人团队吗?云微客助阵,一人即可
现在市面上主流的新媒体平台都进军了短视频赛道,对于众多企业和个人来说,短视频矩阵更是成为了提升影响力和拓展业务的关键。企业或个人可以根据自身产品特点和目标用户群体,构建账号矩阵,在多平台上建立账号矩阵,还可…...
常用语音识别开源工具的对比与实践
常用语音识别开源工具的对比 一.工具概述 1. WeNet 设计目标:WeNet 的设计主要聚焦于端到端(E2E)语音识别,特别是在流式识别方面的优化。其目标是提供一个可以在实际应用中达到低延迟和高精度的系统。模型架构: Con…...
Fortify代码安全测试工具在静态应用安全测试(SAST)方面针对典型问题的改进
Fortify代码安全测试工具作为行业内资深的老牌软件安全测试工具,可以同时支持静态代码扫描和动态代码扫描,本文我们讲述的主要是在静态代码扫描领域Fortify所面临的问题,以及最新的改进。 在应用安全领域,特别是静态应用安全测试&…...
AWS 消息队列服务 SQS
AWS 消息队列服务 SQS 引言什么是 SQSSQS 访问策略 Access Policy示例:如何为 DataLake Subscription 配置 SQS 引言 应用系统需要处理海量数据,数据发送方和数据消费方是通过什么方式来无缝集成消费数据的,AWS 提供 SQS 消息队列服务来解决…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
OpenLayers 可视化之热力图
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 热力图(Heatmap)又叫热点图,是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...
《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
FTP 客服管理系统 实现kefu123登录,不允许匿名访问,kefu只能访问/data/kefu目录,不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
淘宝扭蛋机小程序系统开发:打造互动性强的购物平台
淘宝扭蛋机小程序系统的开发,旨在打造一个互动性强的购物平台,让用户在购物的同时,能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机,实现旋转、抽拉等动作,增…...
掌握 HTTP 请求:理解 cURL GET 语法
cURL 是一个强大的命令行工具,用于发送 HTTP 请求和与 Web 服务器交互。在 Web 开发和测试中,cURL 经常用于发送 GET 请求来获取服务器资源。本文将详细介绍 cURL GET 请求的语法和使用方法。 一、cURL 基本概念 cURL 是 "Client URL" 的缩写…...
Kafka主题运维全指南:从基础配置到故障处理
#作者:张桐瑞 文章目录 主题日常管理1. 修改主题分区。2. 修改主题级别参数。3. 变更副本数。4. 修改主题限速。5.主题分区迁移。6. 常见主题错误处理常见错误1:主题删除失败。常见错误2:__consumer_offsets占用太多的磁盘。 主题日常管理 …...
区块链技术概述
区块链技术是一种去中心化、分布式账本技术,通过密码学、共识机制和智能合约等核心组件,实现数据不可篡改、透明可追溯的系统。 一、核心技术 1. 去中心化 特点:数据存储在网络中的多个节点(计算机),而非…...
