目录
1,树形结构(了解)
1.1 概念
树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。它具有以下的特点:
有一个特殊的结点,称为根结点,根结点没有前驱结点(A节点)
除根结点外,其余结点被分成M(M > 0)个互不相交的集合T1、T2、......、Tm,其中每一个集合Ti (1 <= i <= m) 又是一棵与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个或多个后继
树是递归定义的。
1.2 概念(重要)
结点的度:一个结点含有子树的个数称为该结点的度; 如上图:A的度为6,F的度为3,B的度为0
树的度:一棵树中,所有结点度的最大值称为树的度; 如上图:树的度为6
叶子结点或终端结点:度为0的结点称为叶结点; 如上图:B、C、H、I...等节点为叶结点
双亲结点或父结点:若一个结点含有子结点,则这个结点称为其子结点的父结点; 如上图:A是B的父结点,D是H的父结点......
孩子结点或子结点:一个结点含有的子树的根结点称为该结点的子结点; 如上图:B是A的孩子结点,H是D的孩子结点......
根结点:一棵树中,没有双亲结点的结点;如上图:A
结点的层次:从根开始定义起,根为第1层,根的子结点为第2层,以此类推,即此图有4层。
树的高度:树中结点的最大层次; 如上图:树的高度为4。
树的深度:对于深度,相对于E来说,E的深度是2,I的深度是3,P的深度是4。树的最大深度才能表示数的高度。
树的以下概念只需了解,在看书时只要知道是什么意思即可:
非终端结点或分支结点:度不为0的结点; 如上图:D、E、F、G...等节点为分支结点
兄弟结点:具有相同父结点的结点互称为兄弟结点; 如上图:B、C是兄弟结点
堂兄弟结点:双亲在同一层的结点互为堂兄弟;如上图:H、I互为兄弟结点
结点的祖先:从根到该结点所经分支上的所有结点;如上图:A是所有结点的祖先
子孙:以某结点为根的子树中任一结点都称为该结点的子孙。如上图:所有结点都是A的子孙
森林:由m(m>=0)棵互不相交的树组成的集合称为森林
1.3 树的表示形式(了解)
树结构相对线性表就比较复杂了,要存储表示起来就比较麻烦了,实际中树有很多种表示方式,如:双亲表示法, 孩子表示法、孩子双亲表示法、孩子兄弟表示法等等。我们这里就简单的了解其中最常用的孩子兄弟表示法。
1.4 树的应用
文件系统管理(目录和文件)
2,二叉树(重点)
2.1 概念
一棵二叉树是结点的一个有限集合,该集合:
1. 或者为空
2. 或者是由一个根节点加上两棵别称为左子树和右子树的二叉树组成。
结论:
1. 二叉树不存在度大于2的结点
2. 二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树(有序)
注意:对于任意的二叉树都是由以下几种情况复合而成的:
2.2 两种特殊的二叉树
1. 满二叉树: 一棵二叉树,如果每层的结点数都达到最大值,则这棵二叉树就是满二叉树。也就是说,如果一棵 二叉树的层数为K,且结点总数是 ,则它就是满二叉树。
2. 完全二叉树: 完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n 个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从0至n-1的结点一一对应时称之为完全二叉树(也就是说从上到下,从左到右依次存放)。 要注意的是满二叉树是一种特殊的完全二叉树。
2.3 二叉树的性质
1. 若规定根结点的层数为1,则一棵非空二叉树的第i层上最多有2^(i-1) (i>0)个结点。
比如:第三层为2^2,最多4个结点。第四层为2^3最多八个结点
2. 若规定只有根结点的二叉树的深度为1,则深度为K的二叉树的最大结点数是2^k - 1 (k>0)
如上图深度为4的时候最多有2^4 - 1个,也就是最多为15个结点
3. 对任何一棵二叉树, 如果其叶结点个数为 n0, 度为2的非叶结点个数为 n2,则有n0=n2+1
也就是说度为0的结点会比度为2的结点多一个,如上图度为0的结点有5个,度为2的结点有4个
推导:
4. 具有n个结点的完全二叉树的深度k为
如上图k = 3小了,k = 4大了,所以向上取整,深度就为4
5. 对于具有n个结点的完全二叉树,如果按照从上至下从左至右的顺序对所有节点从0开始编号,则对于序号为i 的结点有:
若i>0,双亲序号:(i-1)/2;i=0,i为根结点编号,无双亲结点
比如 i 是3,(3-1)/ 2 = 1, i 是4,(4-1)/ 2 = 1(已知孩子结点下标求父节点下标)
若2i+1 < n,左孩子序号:2i + 1,否则无左孩子
若2i+2 < n,右孩子序号:2i + 2,否则无右孩子
反过来如果父节点下标是 i ,则左孩子树是2*i + 1,右孩子是2*i + 2。但是如果4下标结点,2*2+2=10 >= n就没有右孩子。左孩子也是如此
练习:
2.4 二叉树的存储
二叉树的存储结构分为:顺序存储和类似于链表的链式存储。(本次用类似于链式存储实现,顺序存储在堆会讲到)
二叉树的链式存储是通过一个一个的节点引用起来的,常见的表示方式有二叉和三叉表示方式,具体如下:
孩子双亲表示法后序在平衡树位置介绍,本文采用孩子表示法来构建二叉树。
即一个结点有三个域:
2.5 二叉树的遍历
前面学习过的顺序表,链表本质上都是按照遍历来完成的,那么二叉树其实也是一样
所以二叉树的遍历分为4种:
我们知道二叉树分为根节点,左子树,右子树,在左子树中又分为根节点,左子树,右子树......
不管是前序,中序,后序都是沿着某条路线进行的
1,前序遍历:根,左,右(A B D C E F)
2,中序遍历:左,跟,右 (D B A E C F)
3,后序遍历:左,右,根 (D B E F C A)
4,层序遍历:从上到下,从左到右依次遍历
2.6 二叉树的实现
1,首先是由一个一个结点组成的,一个结点至少包含三个域,那么使用内部类来创建
2,创建A到H的结点,手动连接。(目前这样创建,实际上不是这样创建,后序会讲)
前序遍历:
中序遍历:
后序遍历:
前序,中序,后序打印:
习题:
结论:根据前序和中序创建二叉树,根据后序和中序创建二叉树。不能根据前序和后序创建二叉树
2.7 二叉树的操作
1,获取树当中结点的个数
写法一:(子问题思路)
写法二:不管是前序,中序,后序遍历会沿着某条线对所有结点依次遍历,即遍历一个++即可(遍历思路)
只要调用了size2,那么nodeSize一定被执行了
2,求叶子结点的个数
子问题思路:
遍历思路:
3,获取第k层结点的个数
4,获取树的高度(树的最大深度)
5,遍历二叉树,找到val所在的结点
6,层序遍历(非递归实现,用于做题)
我们使用队列来实现层序遍历。
7,判断一棵树是否是完全二叉树
2.7 二叉树面试题
2.7.1 检查两颗树是否相同
给你两棵二叉树的根节点 p
和 q
,编写一个函数来检验这两棵树是否相同。
如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。
2.7.2 另一颗树的子树
描述:给你两棵二叉树 root
和 subRoot
。检验 root
中是否包含和 subRoot
具有相同结构和节点值的子树。如果存在,返回 true
;否则,返回 false
。
二叉树 tree
的一棵子树包括 tree
的某个节点和这个节点的所有后代节点。tree
也可以看做它自身的一棵子树。
时间复杂度O(m*n) root为m,subRoot为n
2.7.3 翻转二叉树
描述:给你一棵二叉树的根节点 root
,翻转这棵二叉树,并返回其根节点。
可进行优化,如果左右都为空,不用进行交换 (可减少递归和交换的次数)
2.7.4 判断一颗二叉树是否是平衡二叉树
平衡二叉树 是指该树所有节点的左右子树的深度相差不超过 1。
时间复杂度:当我们走第一步时,求树的高度,时间复杂度为O(N),当我们走第二步时,递归了isBalanced,那么此时又会求一次树的高度,导致一部分代码重复运算,即时间复杂度为
O(N^2)。
优化:我们在求左子树,右子树的高度时,已经知道所返回的高度,如果他们不平衡,就返回一个-1,当root的左边拿到一个负数的时候,说明不平衡,直接返回false
时间复杂度:O(N)
2.7.5 对称二叉树。
描述 :给你一个二叉树的根节点 root
, 检查它是否轴对称。
2.7.6 二叉树的构建及遍历
描述:编一个程序,读入用户输入的一串先序遍历字符串,根据此字符串建立一个二叉树(以指针方式存储)。 例如如下的先序遍历字符串: ABC##DE#G##F### 其中“#”表示的是空格,空格字符代表空树。建立起此二叉树以后,再对二叉树进行中序遍历,输出遍历结果。
也就是说给定一个字符串,这个字符串为二叉树的前序遍历,#号代表空树,我们就可以通过前序遍历把这个二叉树画出来,因为他已经告诉你了空树在什么位置,所以直接通过前序遍历画出二叉树!!!
首先搭好框架:创建结点,传字符串,中序遍历,后面只需完成createTree即可
我们直接以前序遍历的方式创建二叉树:
注意:此时的 i 在这里不会越界,str是一个合法的前序遍历,并不是随便的,且递归的次数和回退的次数是一样的,当你把所有的方法走完之后,那么你的字符串也遍历完了,方法执行完在栈帧上已经销毁了,不可能在继续执行了,所以不会越界!!!
2.7.7 二叉树的分层遍历
层序遍历,期望使用二维数组来打印,第一层,第二层......
层序遍历的变种题:
2.7.8找到两个指定节点的最近公共祖先
描述:给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
解法一:
解法二:
2.7.9 根据一棵树的前序遍历与中序遍历构造二叉树
此时代码会有问题,preIndex是一个局部变量,前序遍历的时候,一直走走走,可能走到H开始返回,此时preIndex是3,一直返回返回,返回到走左树的时候,preIndex可能就是0了,走右树理论上是走到4下标,但是可能是1,就把preIndex不要当作形参进行传递,写成局部变量即可!!!
2.7.10 从中序与后序遍历序列构造二叉树
2.7.11 根据二叉树创建字符串
给你二叉树的根节点 root
,请你采用前序遍历的方式,将二叉树转化为一个由括号和整数组成的字符串,返回构造出的字符串。
空节点使用一对空括号对 "()"
表示,转化后需要省略所有不影响字符串与原始二叉树之间的一对一映射关系的空括号对
2.7.12 二叉树前序非递归遍历实现
2.7.13 二叉树中序非递归遍历实现
中序遍历是先把左边走完才打印val,所以cur一直往左边走,走到cur为空时,弹出D给到top。其他同上。
2.7.14 二叉树后序非递归遍历实现
后序遍历也是如此,当左右都走完才开始打印,左边走完,当cur为空时,不能弹出D,D的右边可能还有树,弹出以后都找不到D了,我们可以peek一下使用top去接收,判断top的right等于null和不等于null两种情况,如果top的right为空,就打印top,并且弹出,如果不为空,cur = top.right
标签:结点,遍历,前序,二叉树,2.7,节点 From: https://blog.csdn.net/2301_81510374/article/details/139904789