树与二叉树(1)

发布时间:2026/10/31 14:39:23

树与二叉树(1) 树基本概念树的定义树是 nn ≥ 0个结点的有限集。当 n 0 时称为空树当 n 0 时称为非空树任意一棵非空树都满足以下条件1.有且仅有一个根结点。2. 当 n 1 时其余结点可分为 mm 0个互不相交的有限集其中每一个集合本身又是一棵树称为子树。树的根结点没有前驱结点除根结点以外的所有结点有且只有一个前驱结点而树中的所有结点可以有多个后继结点。树是一种递归定义的数据结构。基本术语结点间的关系描述类似于人类的亲缘关系1.祖先从根结点到该结点所经分支的所有的结点都是该结点的祖先。2. 子孙以某一结点为根的子树中任意结点都是该结点的子孙。3.孩子/双亲结点所有子树的根结点称为该结点的孩子对应的该结点是孩子的双亲。4.兄弟同一个双亲结点的孩子之前的关系。5.堂兄弟双亲在同一个层的结点是堂兄弟。结点与树之间的属性描述1.度树中一个结点的孩子结点是该结点的度树中结点最大的度数是树的度。2.叶子结点/终端结点度为0结点。3.分支结点/非终端结点度不为0的结点。4.结点的层次从根开始定义根结点是第一层以此往下推5.树的高数/深度树中结点的最大层数称为树的深度/高度。6.有序树和无序树有序树树中的结点按从左到右的顺序结点之间不会随意的调换。无序树则反之需要保证父子之间关系保持不变。7.路径树中的两个结点之间的路径是又这两个结点所经过的结点的序列构成的路径的长度为序列结点个数长度结点树8.边表示双亲和孩子之间的链接关系。一般在图中使用。性质1.树中的结点数所有结点数的度数1根结点数/所有边结点数12.m叉树中第 i 层上至多有m^i−1个结点 i≥ 1m叉树这个树中每一个结点最多有m个孩子至少有0个孩子。3.解释高度为h的m叉树的第h层的每一个分支的叶子结点就是m个这时就为结点树最多的时候所以可以得出第h层的结点数m^k-1假设层数是i则第i层的结点数是m^i-1。当计算从第一层到第i层时是使用等比数列求和公式。二叉树定义二叉树是一个有序树每个结点最多两个孩子最少一个也没有孩子还有左右之分左边的叫左子树右边的叫右子树。二叉树也是一个递归定义的数据结构。特殊的二叉树满二叉树满二叉树一个高度为h的二叉树每层结点树都达到最满合计有2^h -1个结点的二叉树叫满二叉树。满二叉树的叶子结点在最后一层且分支结点的度为均为2。满二叉树是当前⾼度下二叉树结点数的最大的形态。完全二叉树1.高度为h的二叉树前h-1 层都是结点已经到达了最大但是第h层不一定是满的但是满足从左到右结点都是连续的。2.完美二叉树是完全二叉树的特殊情况。3.左边的值表示的是第h层的结点只有一个右边的值表示是完美二叉树4.完全二叉树的叶子一定只出现在树的倒数第一二层并且度为1的结点只有一个其他的结点的度是2或者是0。搜索二叉树与中序遍历有关(左根右)又叫排序二叉树左子树上的值根结点的值右结点的值性质1.在二叉树的第i层之上组最多的结点有 2^(i-1)个。2.高度为h的二叉树至多有2^h -1(h1)3.对任意的二叉树如果度为0结点数是N0度为2的结点树为N2,则N0N21。解释设度为1的结点数是N1.树的结点 度数1 N0N12*N21 结点数 N0N1N2结点是一个一个的所以的树的结点是直接相加而度则是结点下面的所以在度数相加的时算的是结点下面的度把上面的结点没有算进去。4.具有n个结点的完全二叉树的高度为⌊logn⌋ 1二叉树存储结构二叉树可以使用顺序存储结构也可以是链式存储结构。1.顺序存储结构将二叉树每层的结点的元素从右到左从上到下依次存到数组中空结点也要存储在数组中用特殊的元素进行标记这个特殊元素一定是二叉树中没有的。非完全二叉树使用顺序存储会存在一定空间浪费的行为空的结点越多就越浪费。可以使用链式存储的方式。一般l每层结点存储在数组中都是从0开始的有些地方是从0开始的。是0作为开始的 假设x结点的存储位置的下标是i那么x结点的双亲结点的存储位置就是i-1/ 2。x的左孩子结点存储位置的下标是2i1右孩子存储位置的下标是2i2。堆定义小根堆根结点中的值是最小的双亲结点小于或等于孩子结点兄弟之间没有大小之分。大根堆根结点的值最大双亲大于或者等于孩子兄弟之间无大小。两种存放数据的方式可以快速的找到最大值和最小值。堆的插入HeapPush思路将数据插入到最后的位置插入前需要判断一下capacity与size的大小是否是相等的相等说明空间已经满了或者说这里没有空间所以这里需要扩容。在进行扩容时需要注意的是这里是对这个数组进行扩容而不是对这个数据结构结构进行扩容。assert(hp); if (hp-_size hp-_capacity) { int newcapacity hp-_capacity 0 ? 4 : hp-_capacity * 2;//需要扩容到好多空间数 HPDataType* tmp (HPDataType*)realloc(hp-_a, sizeof(HPDataType) * newcapacity);//这里正式开始扩容 if (tmp NULL) { printf(realloc fail\n); exit(0); } hp-_a tmp; hp-_capacity newcapacity; }向上调整算法将插入的结点的值与双亲结点进行比较然后根据大小进行调整。时间复制度O(lgN)然后开始插入数据插入的结点与双亲结点进行比较这里以小根堆为例子注意void HeapPush(Heap* hp, HPDataType x) { assert(hp); if (hp-_size hp-_capacity) { int newcapacity hp-_capacity 0 ? 4 : hp-_capacity * 2;//需要扩容到好多空间数 HPDataType* tmp (HPDataType*)realloc(hp-_a, sizeof(HPDataType) * newcapacity);//这里正式开始扩容 if (tmp NULL) { printf(realloc fail\n); exit(-1); } hp-_a tmp; hp-_capacity newcapacity; } hp-_a[hp-_size] x; hp-_size; //小根堆 int child hp-_size - 1; int parent (hp-_size - 1 - 1) / 2; while (child) { if (hp-_a[child] hp-_a[parent]) { break; } HPDataType tmp hp-_a[parent]; hp-_a[parent] hp-_a[child]; hp-_a[child] tmp; child parent; parent (child - 1) / 2; } }插入的完整步骤把上面的思路拆开来看堆的插入其实只有三步每一步都对应着代码里的一段逻辑检查容量先比较_size与_capacity如果两者相等说明数组已经装满了必须先扩容否则下一步写入就会越界。尾插元素把新元素放到数组的最后一个位置也就是下标_size处然后把_size加一。此时数组的物理结构是完整的但堆的性质可能被破坏。向上调整从新插入的结点出发不断与它的双亲结点比较不满足堆序就交换直到满足堆序或者已经到达根结点为止。向上调整算法AdjustUp向上调整是堆插入的核心它的前提是除了新插入的这个结点之外其余部分已经满足堆的性质。因此只需要沿着从新结点到根结点的这一条路径修复即可不需要动其它分支。以小根堆为例调整规则是如果孩子结点的值小于双亲结点的值就交换两者否则说明已经满足小根堆的性质直接结束循环。//小根堆的向上调整算法 void AdjustUp(HPDataType* a, int child) { int parent (child - 1) / 2; while (child 0) { if (a[child] a[parent]) { break;//已经满足小根堆性质不需要再调整 } HPDataType tmp a[parent]; a[parent] a[child]; a[child] tmp; child parent; parent (child - 1) / 2; } }如果写的是大根堆只需要把判断条件反过来当a[child] a[parent]时结束循环其余逻辑完全一致。几个容易出错的细节循环条件用child 0当child为 0 时说明已经走到根结点根结点没有双亲必须停止否则(0 - 1) / 2在整数除法下会得到 0造成自己和自己比较的死循环。双亲下标用(child - 1) / 2这是顺序存储结构下由孩子下标反推双亲下标的公式与前面二叉树顺序存储中讲的2i1、2i2正好互为逆运算。先判断再交换一旦发现孩子已经不小于双亲就说明整条路径都满足堆序可以提前break不必一路比较到根。扩容只影响数组realloc之后一定要把返回值赋回hp-_a并同步更新_capacity否则下次插入时容量判断会出错。插入后_size必须自增新元素写入_a[_size]之后立刻_size这样_size - 1才是新元素的下标也是向上调整的起点。复杂度分析向上调整最多沿着树的高度走一遍而具有 n 个结点的完全二叉树高度为 ⌊log₂n⌋ 1所以单次插入的时间复杂度是O(logN)。空间上只用了几个临时变量额外空间复杂度为O(1)。需要注意的是这里的 O(logN) 是最坏情况如果新插入的元素恰好不需要移动比如插入的值比双亲大在小根堆中直接满足条件那么一次比较就能结束实际开销会小很多。插入过程示例假设当前小根堆的数组为[10, 15, 30, 40, 50, 100]现在插入元素20尾插后数组变为[10, 15, 30, 40, 50, 100, 20]新元素下标为 6。下标 6 的双亲是(6 - 1) / 2 2值为 30因为 20 30交换数组变为[10, 15, 20, 40, 50, 100, 30]。此时child 2双亲是(2 - 1) / 2 0值为 10因为 20 10满足小根堆性质结束调整。最终数组为[10, 15, 20, 40, 50, 100, 30]堆序依然成立整个过程只比较了两次。与向下调整的区别对比项向上调整插入用向下调整删除堆顶用起点新插入的叶子结点根结点或某个需要下沉的结点方向由孩子向双亲自下而上由双亲向孩子自上而下比较对象只与唯一的双亲比较需要与左右孩子中更合适的那个比较时间复杂度O(logN)O(logN)理解了向上调整后面学习堆的删除HeapPop时向下调整的思路就可以对照着来记一个是从底部往上修一个是从顶部往下修。堆的删除1.在叶子节点进行删除数据又没有什么价值堆的删除是在堆顶进行的删除后使用向下调整算法让他继续保持原来的性质于此同时还可以得到数值的大小。2.删除堆顶元素不用向前挪动覆盖的原则是因为他的效率太低了时间复杂度是O(n);会破坏堆的结构将他重新化成堆结构的成本很大。如何删除将堆顶元素与最后一个叶子结点进行交换然后直接删除叶子节点之后通过向下调度算法把堆变成小根堆或者是大根堆。依图所示反复进行实现向下调度算法//堆的删除HeapPop删除堆顶元素并保持堆的性质 void HeapPop(Heap* hp) { assert(hp); //检查堆结构指针是否为空 assert(hp-_a); //检查底层数组是否已经申请 //1.交换堆顶元素与最后一个叶子结点 HPDataType tmp hp-_a[0]; hp-_a[0] hp-_a[hp-_size - 1]; hp-_a[hp-_size - 1] tmp; //2.删除最后一个叶子结点也就是原来的堆顶元素 hp-_size--; //3.从新的堆顶开始向下调整恢复堆的性质 int parent 0; //向下调整从根结点开始 int child parent * 2 1; //左孩子下标2i1 while (child hp-_size) { //孩子下标越界说明已经到叶子结点调整结束 //选出左右孩子中较小的那个小根堆 if (child 1 hp-_size hp-_a[child 1] hp-_a[child]) { child; //右孩子更小让child指向右孩子 } //如果双亲已经小于等于较小的孩子说明满足小根堆性质不需要再调整 if (hp-_a[parent] hp-_a[child]) { break; } //交换双亲结点和较小的孩子结点 HPDataType a hp-_a[parent]; hp-_a[parent] hp-_a[child]; hp-_a[child] a; //继续向下走一层 parent child; child parent * 2 1; } }
延伸阅读

更多相关文章

2026/10/31 14:39:23

【软考架构】第4章 信息加解密技术、密钥管理、访问控制、数字签名

对称密钥加密算法非对称密钥加密算法数字摘要数字证书(CA签发)密钥管理技术 类似于办理身份证的过程。对称密钥的4种方式就是用来保证发送渠道的,A和B不直接碰面。访问控制技术数字签名信息安全的抗攻击技术三、欺骗攻击与防御 ARP欺骗DNS欺骗…

2026/10/31 14:39:23

30分钟教你搭建普罗监控系统—股票人气榜实时变化!

上次我们已经讲过, 存在很多读者对于它的具体搭建方法以及应用方式缺乏足够的了解, 所以今天我们要结合在一起使用普罗米修斯与采集脚本的方式来进行操作, 来编写一个关于东方财富人气榜的监控系统, 这个规模被设定为较小的程度。跟着这个文章的指引, 静下心来一步一步地往前走…

2026/10/31 14:39:23

python定义全局变量的方法是什么

关于什么是定义全局变量的方法这个疑问。什么是定义全局变量的方法? 这一类的问题, 很可能是我们在平时的学习过程或者是工作场景里面经常能够碰到的。希望通过解决这类问题, 能够让你获得比较多的大收获。下面是我给大家整理出来的可供参考的内容部分, 请大家一起来看看&…

2026/10/31 15:44:27

TRACE32调试器安装配置与USB链路排错指南

简介:《劳特巴赫TRACE32快速入门》是一份面向嵌入式调试初学者与工程技术人员的中文操作手册,围绕LAUTERBACH TRACE32调试器的上手使用展开,帮助读者建立从软件安装、硬件连接到首次启动的完整认知,适用于ARM、TriCore、PowerPC等…

2026/10/31 15:44:27

TRACE32调试器:Debug/Trace链路、安装与PowerView排错

简介:《劳特巴赫TRACE32快速入门》是一份面向嵌入式调试初学者与刚接触劳特巴赫调试器人员的入门手册,并适用于英飞凌等微处理器与微控制器调试场景,重点解决TRACE32软件安装、硬件环境搭建和首次启动中的基础操作与常见疑问。压缩包内为1个P…

2026/10/31 15:44:27

Gemini 4 Argon 优先开放安全团队:大模型分发策略与安全落地实践

1. 这次发布到底特殊在哪谷歌放出了 Gemini 4 Argon,圈子里讨论最多的不是跑分,而是它的分发策略——最强的那一档能力,先给安全团队用,普通开发者哪怕付了钱也得排队等。这个做法在模型发布史上不算常见,值得拆开聊聊…

2026/10/31 15:44:27

MCP协议深度解析:340个插件生态与AI工具调用标准化实践

1. 从340个包说起:MCP生态到底在发生什么 第一次看到“340个包”这个数字的时候,我的反应是——这个量级已经不能用“尝鲜”来解释了。一个插件目录能攒到三百多个条目,说明它已经跨过了“开发者自娱自乐”的阶段,进入了“有人真的…

2026/10/31 15:44:27

2026年AI技术趋势:神经符号系统与生物启发式计算

1. 2026年AI技术全景展望 2026年4月13日这个时间节点具有特殊意义——它标志着生成式AI技术发展进入第十个年头。从2016年AlphaGo战胜李世石开始,AI技术已经完成了从实验室到产业化的关键跨越。站在这个时间点上回望,我们会发现几个显著的技术演进轨迹&a…

2026/10/31 15:39:26

基于ResNet18的鸟类识别系统设计与优化实践

1. 项目背景与核心价值鸟类识别作为计算机视觉领域的经典应用场景,在生态监测、生物多样性研究、智能观鸟设备等领域具有广泛的应用前景。传统鸟类识别主要依赖专家人工观察,效率低下且成本高昂。基于卷积神经网络(CNN)的自动化识…

2026/10/30 20:55:10

全国计算机二级C语言真题PDF高效刷题指南

简介:这份PDF是全国计算机二级C语言考试的试题与答案解析,专为备考二级C语言的考生准备。内容涵盖C语言特点与设计理念、程序基本组成单位、main函数执行流程、函数嵌套调用规则、标识符命名规范、整型实型字符型数据类型、八进制十六进制数合法形式、运…

2026/10/30 20:55:10

requests+BeautifulSoup:爬虫抓取h1标题的入门实战指南

简介:一份以实战为导向的Python爬虫入门资源,面向具备基本编程基础、希望快速掌握网络数据抓取的初学者,可解决从零编写简单爬虫的常见困惑。实例以requests库发送HTTP请求、BeautifulSoup解析HTML文档,完整演示从网页中提取 标题…

2026/10/30 20:55:09

递推最小二乘参数辨识MATLAB编程:二阶系统与渐消记忆实现

简介:这是一篇面向控制系统、信号处理与机器学习等领域学习者的最小二乘参数辨识程序说明文档。资源共包含一个docx格式文档,压缩包大小七十八KB,内容围绕递推最小二乘算法和渐消记忆最小二乘算法展开:先构造一个极点位于单位圆内…

2026/10/31 0:02:41

猫狗目标检测数据集:VOC/COCO/YOLO三格式一体化实战包

简介:本资源是一套面向计算机视觉初学者与YOLO目标检测实践者的猫狗图像数据集及配套开发工具包,专为快速上手目标检测模型训练而设计。资源包含1000张真实场景高清猫狗图片,标注质量高,同时提供VOC(XML)、…

2026/10/31 0:02:41

HDU2017多校标程复现:算法竞赛工程化真题沙盒

简介:本资源为2017年HDU多校联合训练第一场官方配套资料,面向ACM程序设计竞赛备赛学生、算法爱好者及高校编程集训队成员,旨在提供权威解题参考与完整测试验证环境。压缩包共40个文件,含15份C标程(覆盖全部赛题&#x…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑