树形结构--二叉树以及二叉树的遍历(十七)
一.二叉树
1.二叉树的定义
把满足以下条件的树结构称为二叉树:
1.每个结点的度都不大于2.
2.每个结点的孩子结点次序不能任意颠倒。
2.二叉树的性质
性质1:在二叉树的第k层上,最多有2^(k-1)(k≥1)个结点。
性质2:深度为m的二叉树最多有2m-1(m≥1)个结点。
性质3:在任意一棵二叉树中,如果其终端结点数为n0,度为2的结点数为n2,则n0=n2+1。
性质4:具有n个结点的二叉树,其深度至少为〔log2n〕+1,其中〔log2n〕表示取log2n的整数部分。
性质5:如果对一棵有n个结点的完全二叉树按照从上到下和从左到右的顺序对所有结点进行从1开始顺序编号,则对于任意的序号为i的结点有:
①如 i =1,则结点i是二叉树的根,无双亲,如 i > 1,则序号为i的结点的双亲结点序号为i/2。
②如 2i > n,则序号为i的结点无左孩子,如 2i ≤ n,则序号为i的结点的左孩子结点的序号为2i。
③如 2i+1 > n,则序号为i的结点无右孩子,如 2i+1 ≤ n,则序号为i的结点的右孩子结点的序号为2i+1。
3.二叉树的存储结构
二叉树的结构是非线性的,每一个结点最多可有两个后继。二叉树的存储结构分为顺序存储结构和链式存储结构。
1).顺序存储结构
顺序存储结构可以用一维数组来实现,如下图:
可以看出,当二叉树为完全二叉树时,这种存储结构很方便,而且不浪费空间,但是对于一般的二叉树,必须用“虚结点”将其补成一个完整的完全二叉树来存储,这就造成了空间浪费,如下图:
因此,顺序存储一般适用于完全二叉树,既然顺序存储不能满足对二叉树的存储需求,那么可以使用链式存储,如下图。
2).链式存储结构
对于任意二叉树,每个结点只有一个双亲结点(根除外),最多有两个孩子,便可以设计成上图所示结构:【lchind data rchild】。该二叉树实现方式又称为二叉链表。
3).结点代码实现
二叉链表结点结构代码实现如下:
typedef struct Node
{
DataType data;
struct Node * LChild;
struct Node * RChild;
}BiTNode,*BiTree;
二.二叉树的遍历(重点)
1.遍历理论过程
如果用LDR表示遍历左子树,根结点,右子树,则有如下6种遍历顺序:
①访问根,遍历右子树,遍历左子树。记作DRL
②访问根,遍历左子树,遍历右子树。记作DLR
③遍历左子树,访问根结点,遍历右子树。记作LDR
④遍历右子树,访问根结点,遍历左子树。记作RDL
⑤遍历左子树,遍历右子树,访问根结点。记作LRD
⑥遍历右子树,遍历左子树,访问根结点。记作RLD
如果规定按先左后右的顺序遍历,则只剩下三种:DLR , LDR , LRD。
然后根据对根访问顺序的不同,分别称DLR为前序遍历,LDR为中序遍历,LRD为后序遍历。
对于上述的二叉树: 前序遍历顺序:A B D E C F G 中序遍历顺序:D B E A F C G 后序遍历顺序:D E B F G C A
写成这样,我觉得会更有助于萌新的理解:
前序遍历顺序:(A) (B (DE) ) (C (FG) ) 中序遍历顺序:((D) B (E)) (A) ((F) C (G)) 后序遍历顺序:( (DE) B) ( (FG) C) (A)
先将大头写下来然后进行填写,类似于填空,但是仅限于帮助你理解遍历顺序,而不是让你使用这个方法记忆遍历顺序,我们应该通过辅助工具来帮助我们真正了解。
下面再出一个练练手:
试着写一写是不是和下面的答案一样呢,如果一样,说明你学会了! 前序遍历:A B D F G C E H 中序遍历:B F D G A C E H 后序遍历:F G D B H E C A
下面用代码来实现各种遍历。
2.前序遍历
void PreOrder(BiTree root)
{ // root为指向二叉树或某一树的根结点
if(root!=NULL)
{
Visit(root->data); // 访问根结点,显示数据结点
PreOrder(root->LChild);// 遍历左子树
PreOrder(root->RChild);// 遍历右子树
}
}
3.中序遍历
void PreOrder(BiTree root)
{
if(root!=NULL)
{
PreOrder(root->LChild);
Visit(root->data)
PreOrder(root->RChild);
}
}
4.后序遍历
void PreOrder(BiTree root)
{
if(root!=NULL)
{
PreOrder(root->LChild);
PreOrder(root->RChild);
Visit(root->data);
}
}
虽然理论知识对于二叉树的遍历很复杂,但是由于二叉树是一种递归定义的结构,先序,中序,后序,便是递归定义的。故采用递归的方式遍历二叉树的代码量很少。
若有错误,欢迎指正批评,欢迎评论。 每文一句:没有人会为你的贫穷负责,却有人为你的富有而喝彩!所以不要活在别人的嘴巴里,做好自己!有路,就大胆地去走;有梦,就大胆地飞翔;前行的路,不怕万人阻挡,只怕自己投降!
- 一个二进制POC的诞生之旅CVE-2018-0802
- 远程RPC溢出EXP编写实战之MS06-040
- 浮点数加法引发的问题:浮点数的二进制表示
- 新手科普 | MySQL手工注入之基本注入流程
- linux 系统监控、诊断工具之 lsof 用法简介
- 关于 SimpleDateFormat 的非线程安全问题及其解决方案
- 关于 WEB/HTTP 调试利器 Fiddler 的一些技巧分享
- Java线程使用技巧学习(一)
- Python FAQ(常见问题解答)(1)
- ElastAlert监控日志告警Web攻击行为
- Java线程使用技巧学习(二)
- 挖洞经验 | 看我如何发现“小火车托马斯”智能玩具APP聊天应用漏洞
- Hive 常见问题与技巧【Updating】
- Hive 基础(1):分区、桶、Sort Merge Bucket Join
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- BFE.dev前端刷题88 - 在JavaScript中实现负索引
- Springboot 原理
- Qt音视频开发17-海康sdk解码
- pytest文档47-allure报告添加用例失败截图
- [060]监听应用的前后台切换
- 借用 potplayer 播放器,在本地播放 b 站视频也能看弹幕了
- 非Spring项目管理Quartz
- ArrayList并发写出现Null值
- 那些有趣的网站系列(四)
- 纯CSS实现吸附效果
- Hexo-Matery主题性能优化
- 精选MAC应用推荐,让你搬砖效率翻倍!
- 【JAVA基础&高级】“面向对象篇” 知识点汇总
- pycharm 入门基础配置
- CS学习笔记 | 16、用户枚举三个关键步骤