我们知道如何构建一个图的数据结构,那么现在,我们看看如何根据一棵图的图形,创建一张图到内存中去。
1 |
|
数据结构中
1 | void create_MG(MGraph *MG) |
我们一部分一部分的讲
1 | //初始化邻接矩阵,双层循环将二维数组都填充为0 |
这里我们先把矩阵创建出来
1 | //定位 |
这个定位的函数,主要是
1 | //输入边的信息,建立邻接矩阵,有多少边执行多少次 |
有多少个顶点就循环多少次
1 |
|
V0 | V1 | v2 | v3 | |
---|---|---|---|---|
V0 | 0 | 1 | 1 | 1 |
V1 | 1 | 0 | 1 | 0 |
V2 | 1 | 1 | 0 | 1 |
V3 | 1 | 0 | 1 | 0 |
我们将上面的图输入程序,我们用 a、b、c、d 代替v1、v2、v3、v4
运行结果
请输入图的类型,有向图输入1,无向图输入0:0
图有几个顶点:4
图有几条边:5
请输入第1个结点的值:a
请输入第2个结点的值:b
请输入第3个结点的值:c
请输入第4个结点的值:d
请输第1个边的信息:a b
请输第2个边的信息:b c
请输第3个边的信息:c d
请输第4个边的信息:d a请输第5个边的信息:a c
图类型:无向图
图中的顶点有: 4 个
图中的边/弧有: 5 个
顶点的集合:a b c d
邻接矩阵:
a 0 1 1 1
b 1 0 1 0
c 1 1 0 1
d 1 0 1 0
运行结果同矩阵相同,成功!
这是我的个人学习笔记,主要是应付考研复习使用,充斥着一些吐槽和个人观点,并不严谨,欢迎大家参考、指正。
]]>学了那么多图的知识,是时候动手设计一下图的存储结构了,图跟线性表、树有那么大得差别,所以图的数据结构要复杂很多。
我们先回忆一下线性表和树得存储结构
那么图呢?
我们观察下面的四张图
这四张图形态各异,但是
都是一张图。
那么该如何将图在内存中表示呢?
想明白上面得问题,我们就来回答这个问题
邻接矩阵方案
,撒花!图的邻接矩阵(Adjacency Matrix):存储方式是用两个数组来表示图。
我们试着来存放上面这个无向图,首先设计一个一维数组 vexs 存放顶点信息
vexs0 | vexs1 | vexs2 | vexs3 |
---|---|---|---|
V0 | V1 | V2 | V3 |
接着设计一个邻接矩阵(二维数组)arcs,存放边和边得信息
V0 | V1 | v2 | v3 | |
---|---|---|---|---|
V0 | 0 | 1 | 1 | 1 |
V1 | 1 | 0 | 1 | 0 |
V2 | 1 | 1 | 0 | 1 |
V3 | 1 | 0 | 1 | 0 |
因为是无向图,所以
我们设计这个存储结构时
所谓对称矩阵,就是
有了这个数据结构,我们就可以很轻松的知道
V0 | V1 | v2 | v3 | |
---|---|---|---|---|
V0 | 0 | 1 | 1 | 1 |
V1 | 1 | 0 | 1 | 0 |
V2 | 1 | 1 | 0 | 1 |
V3 | 1 | 0 | 1 | 0 |
V1 的度是2,因为无论行、列,为1的元素加起来,都是2
V1 的邻接点是 V0、V2,因为 V1 行只有 V0、V2 的元素为1
我们看看图对不对,没有问题!
我们在无向图中发现,邻接矩阵竟然是对称的,原因也很简单
有向图最重要的一点就是区分方向,我们按照上面的思路,首先设计一个一维数组 vexs 存放顶点信息
vexs0 | vexs1 | vexs2 | vexs3 |
---|---|---|---|
V0 | V1 | V2 | V3 |
接着设计一个邻接矩阵(二维数组)arcs,存放边和边得信息
V0 | V1 | v2 | v3 | |
---|---|---|---|---|
V0 | 0 | 0 | 0 | 1 |
V1 | 1 | 0 | 1 | 0 |
V2 | 1 | 1 | 0 | 0 |
V3 | 0 | 0 | 0 | 0 |
因为我们要注意方向
我们设计这个存储结构时
有向图有入度和出度的概念,所以
V0 | V1 | v2 | v3 | |
---|---|---|---|---|
V0 | 0 | 0 | 0 | 1 |
V1 | 1 | 0 | 1 | 0 |
V2 | 1 | 1 | 0 | 0 |
V3 | 0 | 0 | 0 | 0 |
V1 的入度是1,出度是2
没有问题,我们接下来实际设计一下数据结构。
我们在上一节,说到了网这个概念,实际上就是每条边上带有权的图,就是网。
网最重要的一点就是看好权值,我们按照上面的思路,首先设计一个一维数组 vexs 存放顶点信息
vexs0 | vexs1 | vexs2 | vexs3 |
---|---|---|---|
V0 | V1 | V2 | V3 |
接着设计一个邻接矩阵(二维数组)arcs,存放边和边得信息
V0 | V1 | v2 | v3 | |
---|---|---|---|---|
V0 | $\infty$ | $\infty$ | $\infty$ | 18 |
V1 | 8 | $\infty$ | 2 | $\infty$ |
V2 | 4 | $\infty$ | $\infty$ | $\infty$ |
V3 | $\infty$ | $\infty$ | $\infty$ | $\infty$ |
这里有三种情况
1 | typedef enum { DG, UDG } GraphType; //声明图的类型,有向还是无向 |
好了,理解了上述概念,我们下节课来讲,如何创建一个图。
这是我的个人学习笔记,主要是应付考研复习使用,充斥着一些吐槽和个人观点,并不严谨,欢迎大家参考、指正。
]]>前面我们基本讲了什么是图结构,以及顶点、边和弧之间不同状态的特殊图结构,我们今天继续,看看这些顶点、边和弧之间的关系。
我们来讲讲顶点和边的一些关系
我们说,如果存在一个无向图
,G(V,E)
邻接点(Adjacent)
相邻接,不是连接!
。顶点 V 的度(Degree)是和 V 相关联的边的数目,记为 TD(V)
那么在有向图中的度呢?现在有有向图 G=(V,E)
注意是尖括号。
属于边的集合E邻接自
和邻接到
是不同的,请注意!所以,有向图就跟无向图不同了
我们看上面的有向图中
在图中,我们从一个顶点到另一个顶点的路线,称为路径(Path)
以下四种是在无向图 G 中顶点 A 到顶点 D 的四种不同的路径
我们看到顶点到顶点之间有这么多种不同的走法
以下是在有向图 G 中从 B 到 D 的路径
有向图的路径需要额外注意方向
路径的长度就是路径上边或弧的数量。
如果从第一个顶点到最后一个顶点相同的路径,我们称之为环
既然一个顶点到最后一个顶点得路径如果相同,我们就称之为环,那么什么是简单环呢?
简单环 | 不是简单环 |
---|---|
右侧图不是简单环得原因是
如果在一个无向图中
不是连通图 | 连通图 |
---|---|
为什么左边得图不是连通图呢?因为
注意:
完全图是:任意两个顶点必须是连接得,也就是得有边。
连通图是:任意两个顶点只要有路径能连过去就行。
无向图中的极大连通子图称为连通分量。
图 G | 极大连通图-1 | 极大连通图-2 | 不是极大连通图 |
---|---|---|---|
我们看上面的图
我们说的连通图是无向图,对于有向图来说
强连通分量。
不是强连通图 | 强连通图 |
---|---|
为什么左侧得不是强连通图?
但是右侧得可以是左侧图到极大强连通子图
连通图生成树,顾名思义,通过连通图生成一棵树,连通图的生成树是一个极小的连通子图,
为什么呢?因为:
普通图 G | 极小连通子图/树 | 极小连通子图/树 | 不是极小连通子图/树 |
---|---|---|---|
中间两个图,都符合以下条件
极小连通子图,也是连通图生成树
最右边的图,不符合以下条件
如果一个有向图
,符合以下条件
一棵有向树。
上面两张图不是有向树,左图因为 D 结点的入度为3,右图因为 A 结点入度为2
上图是有向树
这是我的个人学习笔记,主要是应付考研复习使用,充斥着一些吐槽和个人观点,并不严谨,欢迎大家参考、指正。
]]>终于,我们迎来了新的数据结构,图结构。之前的线性表,主要是一对一的关系,一个元素和另一个元素的关系。树结构,主要层与层之间一对多的关系,即一个结点可以有 N 个子树,N 个子树也只对应一个双亲结点。那么大家也应该猜到了,图解决的是多对多的数据关系。
(顶点1有一个环的图结构)
图(Graph):是有顶点的又穷非空集合和顶点之间的边集合组成。
对于上面的图
我们回顾一下线性表和树的数据
我们回顾一下线性表和树没有数据元素时
顶点集合 V 要有穷非空。
我们回顾一下线性表和树中数据元素之间的关系
图中的各种特性和定义比较多,我们分开讲
看着上面这张图,我们来说明一下
边没有方向
无向边(Edge)
知道了上面的概念之后,我们就可以说
无向图
,如果 $G1=\left { V1 , E2\right }$看着上面这张图,我们来说明一下
边有方向
有向边,也称为弧(Arc)
知道了上面的概念之后,我们就可以说
有向图
,如果 $G2=\left { V2 , E2\right }$什么叫做简单图呢?在一个图结构中
以下两个都不是简单图
同一条边出现两次 | A 不能指向自己 |
---|---|
注意:我们目前讲的主要都是简单图,因为应用广泛。
根据图边、弧和顶点的不同情况,又分为以下几种常见图
如果在一个无向图中
如果在一个有向图中
这里讲的稀疏和稠密都是相对而言的,通常
这里说的权根我们在哈夫曼树中说的权是一样的
权(weight)
带权图为网(NetWork)
如果有两个图 G1=(V1,E1)和 G2=(V2,E2)
上面两张图中,红框内的都是左边图的子图。
记住有向图还要关注方向是否相同!
基础知识还有不少,我们下节继续!
这是我的个人学习笔记,主要是应付考研复习使用,充斥着一些吐槽和个人观点,并不严谨,欢迎大家参考、指正。
]]>]]>未完待续…
]]>未完待续…
上一节我们发现,树、森林和二叉树之间竟然有这么多有趣的关联,前根和后根遍历结果竟然都可以跟二叉树相同。二叉树竟然这么的独特,今天我们就来看看,如何打造一棵完美二叉树!
我们在使用二叉树的时候,会遇到类似这样的问题
要弄清楚这个问题,我先首先要弄清楚刚才哪些加了数字的结点是什么。
还是这张图,我们看到这棵二叉树有一点点不同
结点间连线的相关书叫做权。
我们引申出一个新定义,结点的路径长度,我们来看一下
定义:根结点到叶子结点的路径上的连接数。
上图中,C 结点的路径长度为 3。
定义:根到每一个结点的路径长度之和。
上图中,我们来算一下
树的路径长度为9。
结点的路径长度是根到结点,那么带上权呢?
定义:结点的路径长度与结点权值的乘积。
我们还是拿 C 结点举例
定义:根到每一个结点的路径长度乘以权值的和。
上图中,我们来算一下
树的带权路径长度为275。
我们将树的带权路径长度称为 WPL(Weighted Path Length)
记住,WPL 就是树中所有叶子结点的带权路径长度之和。
我们称 WPL 的值越小,构造出来的二叉树就约优秀,那么该如何构造出最优的二叉树呢?对了,最优二叉树我们也称为哈夫曼树!
首先,我们在森林中选择两课根结点权值最小的树,上图所示
接着我们来操作上面的两个最小的结点
我们剩下的还有两个结点,A 和 B
我们开始比较 B 和 N1
最后剩下的结点 A 相同,最后得到这棵哈夫曼树的根权值为18。
哈夫曼树构造完毕。
我们费劲儿构建的这棵哈夫曼树有什么厉害的地方呢?
完美二叉树。
还记得我们开头这个问题吗?既然访问节点的概率就是权值的话,我们用哈夫曼树的构建方式,将 A B C D 重新构建之后,就可以解决啦!
这是我的个人学习笔记,主要是应付考研复习使用,充斥着一些吐槽和个人观点,并不严谨,欢迎大家参考、指正。
]]>我们学习了树、二叉树、以及森林的知识。我们直到遍历二叉树非常的方便,甚至学会了给二叉树增加线索,提高效率。那么问题来了,如果一棵树,不是二叉树,一个结点下有 N 个孩子,那么我们遍历和操作起来,就会很复杂,因为不知道这棵树是深度长还是宽度广,如果都能转换成二叉树来操作就爽了!今天我们就看看普通树和森林,如何转换成二叉树。
既然二叉树最方便操作,我们如何能够将普通树转换成二叉树呢?
第一步,操作兄弟结点
第二步,去掉原有的连接线,规则如下
第三步,变成二叉树
森林转换为二叉树,其实跟刚才的普通树转换为二叉树差不多
我们来看一个例子
这片森林有三棵树,每棵树都不是二叉树,我们将其先都转换成二叉树的形式
然后我们把三棵树的根连在一起,每棵树的根,都是右子树
然后我们根据普通树转二叉树的规则,再加上每棵树的根都是右子树,将森林转换成二叉树
第一步:我们知道如何将二叉树转换成树和森林,我们接着看看,如何反向转换
这张图就是根据上面的规则进行连线,我们来看一下
第二步:去掉所有双亲到右孩子之间的连线
注意,G 到 I 是可以的,因为 H 才是 I 的双亲。
最后我们把二叉树转换回了森林。
注意:判断二叉树是森林还是普通树的方法是看二叉树的根节点有没有右子树,如果有,那根据倒推原理,一定是森林,没有就是普通树。
普通树转换为二叉树
森林转换为二叉树
二叉树转换为树、森林
我们为什么费劲把普通树和森林转换为二叉树呢?
因为普通树和森林遍历不方便。
如果我们使用先根
的方法遍历普通树
同样的,如果我们使用后根
的方法遍历普通树
我们看看例题
先根遍历结果是:A B E F C G D H I J
后根遍历结果是:A B E F C G D H I J
其实森林的遍历跟树的遍历一样
前人们惊人的发现了以下事实,对于森林、树的遍历和二叉树的遍历
好了!有意思的事情来了!下节继续!
这是我的个人学习笔记,主要是应付考研复习使用,充斥着一些吐槽和个人观点,并不严谨,欢迎大家参考、指正。
]]>这是一篇在 Nintendo Switch 6.2.0系统上,将游戏数据和存档从 128G 内存卡 转移 到 256G 内存卡,转移成功后,下载的游戏数据及存档有被 Switch 系统认到,并可正常使用的。
前天下了《任天堂明星大乱斗 特别版》的时候,发现自己去年11月买的 128G microSD 卡竟然满了,以为能用一段时间呢… 今天进入 Manage Software 一看《DOOM》竟然要 30G ,《异度之刃2》、《任天堂明星大乱斗 特别版》、《Diabol III》都要十几个 G ,所以入了 256 G 的新 microSD 卡,老卡就留着之后进军 Vlog 吧。
至于为什么要写这篇文章呢?因为发现网上流传的几种方法我复现都失败了,也算是记录一下,万一下次换 512G 的内存卡呢?废话不多说,老 128G microSD 卡换256G,游戏文件和存档该如何转移呢?我们接着看~
我们在开始前,请确认下面的东西已经准备好了
我们的大概思路如下
格式化新内存卡
,让 Switch 认到数据拷贝到新内存卡
新内存卡 Switch 还能认到。
我 Google 了一下目前网络上的换内存卡的方法,大致有三种
(你如果只想解决问题,可以跳过碰壁这一节)
第一种不推荐方法
昨天刚换了
先关机 把旧卡nintendo文件夹拷贝到电脑上
插入新卡开机,进系统后识别到新卡后会看见在卡上的数字版游戏会出现云图标,再关机拔新卡
然后把nintendo文件夹覆盖到新卡上
新卡插回机器,开机后游戏仍然有云图标,点进去会让你下载,不过是瞬间就下好了 因为数据是有的
来源:https://bbs.nga.cn/read.php?tid=12603922&rand=638
不推荐原因
第二种不推荐方法
1把老卡nintendo文件夹全部拷贝到电脑上;
2 把新卡放进任天堂Switch中,此时,NintendoSwitch会提示你重启,确定重启,然后拔出新卡,ns会关机;
3新卡连上电脑,将之前拷贝的nintendo文件夹复制到新卡根目录 最后百分之几的时候会提示有相同内容,选择全部覆盖;
4将任天堂Switch开机,所有游戏图标都有个云下载提示,不要管它,插入拷贝好的新卡,提示重启,确定—然后就ok了。
PS:这里一定要让NintendoSwitch在开机状态下插入新卡自己重启一次,如果关机状态插入新卡,开机以后进所有游戏都会报错。
来源:https://zhuanlan.zhihu.com/p/49452671
不推荐原因
第三种不推荐方法
1把老卡nintendo文件夹全部拷贝到电脑上;
2 把新卡放进任天堂Switch中,此时,NintendoSwitch会提示你重启,确定重启,然后拔出新卡,ns会关机;
3新卡连上电脑,将之前拷贝的nintendo文件夹复制到新卡根目录 最后百分之几的时候会提示有相同内容,选择全部覆盖;
4将任天堂Switch开机,所有游戏图标都有个云下载提示,不要管它,插入拷贝好的新卡,提示重启,确定—然后就ok了。
PS:这里一定要让NintendoSwitch在开机状态下插入新卡自己重启一次,如果关机状态插入新卡,开机以后进所有游戏都会报错。
来源:https://bbs.a9vg.com/thread-5284744-1-1.html
不推荐原因
当然了:上面三种方法,我都没能成功,有运气因素,不过如果我下面的方法失效了,你们也可以试试上面的方法。
我们开始吧,我标红的地方一定要着重看,顺序、先后很重要!
我们先操作 Switch
我们将老内存卡先进行备份,防止数据丢失
高速读卡器
,否则等到天荒地老…在等待的过程中,我们先来准备新卡
在 Switch 关机状态下插入新内存卡
点击齿轮图标,进入设置。
左边列表向下划最后,进入 System,在System 界面最下面找到初始化(Formatting Option)
在初始化(Formatting Option)里,选择格式化 microSD 卡(Format microSD card)
注意
点击 Continue,将新卡格式化
。
这一步是至关重要的
,因为如果 Switch 不认,数据拷贝到老卡上也没用
我估计我上面三种方法失败的原因就出在这一步。
不插任何内存卡开机
。老内存卡插入 Switch
,正常会提示如下选择later
,然后在开机状态下拔出老内存卡
,不会有任何提示将新内存卡也插入
,正常会提示如下这里同样选择later
,然后关闭 Switch
,将新内存卡拔出
将执行完上面操作的新内存卡,连接读卡器
打开刚才在PC / Mac 上备份的Nintendo 文件夹,里面是三个文件
将 Album 文件夹中的以年份开头的所有文件,复制到新内存卡的 Album 文件夹中
将 Contents 文件夹中的 private 文件,复制到新内存卡的 Contents 文件夹中
将 Contents 文件夹中的 placehld 和 registered 文件夹中的所有内容都复制到新内存卡对应的文件夹中,切记不要直接复制 placehld 和 registered 这两个文件夹,只操作 placehld 和 registered 文件夹中的文件。
将 save 文件夹中的所有文件,复制到新内存卡的 save 文件夹中
特别注意
文件夹内
的文件,不要操作整个文件夹。文件重复,直接覆盖文件即可。
上述文件全部操作完毕后
Switch关闭电源
,拔出里面的内存卡不插内存卡状态开机
已经开机的 Switch 主机中
选择 restart ,重启 Switch,此时
没有云图标
恭喜你!大功告成!
至于为什么几十字的东西我要说的这么啰嗦,是因为你觉得你会初始化内存卡、你知道该如何备份数据,你很优秀!你可以跳过!如果我不写清楚,不清楚的又要去搜来搜去,会更麻烦!然而他们也很优秀,他们没法不跳过。他们只是想知道这些啰嗦。
]]>上一节,我们试着解决了树在存储时空间浪费和没法找到双亲结点的问题,这一节,我们着重看一下如何用代码实现。这篇难度有一点大,请务必沉住气,认真仔细。
我们一步一步的拆解代码来看看。
我们首先声明我们重新设计的线索二叉树结点
struct BiTNode
,代表该结构体struct BiTNode *
,代表结构体指针1 |
|
我们刚才重新设计了结点,现在开始生成树,首先接收数据
BiTree *tempTree
BiTree *tempTree
你可以理解为 struct BiTNode **tempTree
指针的指针。
然后我们判断 scanf 中的数据
*tempTree
设置为 NULL,不是** tempTree
如果scanf 中接受的不是#号
(*tempTree)
这里为什么加括号呢?学过指针都知道!指针的指针
,所以把 tempTree 加一个星号1 | void Create(BiTree *tempTree){ |
我们生成了一个二叉树,这棵二叉树的每个结点都有 Ltag 和 Rtag,该如何利用呢
判断根节点左子树的情况
判断根节点右子树的情况
1 | BiTree pre; |
我们知道,根结点是没有双亲的,那么怎么获得根结点的前驱呢?
记得收尾
1 | void Inorderthread(BiTree t , BiTree *p){ |
用递归的时候注意,要在加线索之前使用,否则树都连起来就会出错。
###递归中序遍历,不多说了,需要的话看前面
1 |
|
同样是左中右
1 | void Inorderthreading(BiTree t){ |
这个其实没什么好讲的,就是星号和顺序的问题
&
符号1 | int main(){ |
这个也绕了我好久,主要问题是
1 |
|
输出结果:
请输入结点:
ABDH##I##E#J##CF#K##G##
递归-中序遍历是:HDIBEJAFKCG
迭代-中序遍历是:HDIBEJAFKCG
遍历的是上面这棵树。
我第二天一早又写了一遍,加了一些注释
1 |
|
这是我的个人学习笔记,主要是应付考研复习使用,充斥着一些吐槽和个人观点,并不严谨,欢迎大家参考、指正。
]]>前面,我们说过,二叉树的遍历方法,以及如何使用代码创建二叉树并将三种遍历顺序都输出出来。如果你还对单链表有印象,我们会发现上节我们树的创建过程其实就是根结点指向左右子树,很像单链表指向唯一后继,对不对?还记得为什么我们要使用双向链表嘛?因为双向链表在找寻唯一前驱的时候方便。因此,二叉树也遇到了相同的问题,左右子树不知道自己的双亲是谁,我们该怎么解决呢?
现在的二叉树
如果我要找这个结点的双亲该怎么办呢?
我们看上面这张图,发现
好了,现在的问题是
那么把剩余的空间利用起来,存放结点的双亲信息不就好了?
我们将原有的结点结构体,改造如下
左孩子 | 左孩子状态 | 数据 | 右孩子状态 | 右孩子 |
---|---|---|---|---|
Lchild | Ltag | Data | Rtag | Rchild |
我们发现,增加了左右孩子的状态
状态为0
,那么左孩子指向的是左孩子的结点地址。
状态为1
,那么左孩子指向的是该结点的前驱地址。
状态为0
,那么右孩子指向的是左孩子的结点地址。
状态为1
,那么右孩子指向的是该结点的后继地址。
这么说可能有点晕,我们看看这张图,我们看到
左孩子指针域
存储的就是H 结点的前驱
右孩子指针域
存储的就是H 结点的后继
这样我们就最大限度上利用了存储空间,提高了搜索结点时的效率。即用空间换时间。下节我们主要讲讲线索二叉树的代码。
这是我的个人学习笔记,主要是应付考研复习使用,充斥着一些吐槽和个人观点,并不严谨,欢迎大家参考、指正。
]]>上一节,我们详细说明了二叉树的 4 种遍历方式,根据根的位置不同,我们分为前序遍历、中序遍历和后序遍历以及最常用的层序遍历。今天我们就来实战一下, 看看如何建立一棵树,并且按照我们需要的方式遍历树上的结点。
首先要构建一棵树,因为是二叉树,所以结构体中包含左右两棵子树
data
指针域
1 |
|
接着,我们开始创建树,我们使用的是返回树根节点地址的方式
1 |
|
树的遍历,我们使用递归的思路,其实很好遍历
1 | void PreVisitTree(struct BiTNode *Temptree){ |
主调 Main 函数
1 | int main(){ |
1 |
|
输出结果
请输入前序结点:
ABDH##I##E#J##CF#K##G##
ABDHIEJCFKG
HDIBEJAFKCG
HIDJEBKFGCA
这是我的个人学习笔记,主要是应付考研复习使用,充斥着一些吐槽和个人观点,并不严谨,欢迎大家参考、指正。
]]>这节课是二叉树的考试重点,通过什么样的方式遍历二叉树,这里需要把内容搞清楚,不要混淆。
二叉树遍历(traversing binary tree),指的是
照某种次序
依次访问
二叉树中的所有结点仅有一次
。线性结构的便利,最多是
树就不一样了
如果都是从左到右的话,遍历方式分为 4 种
如果二叉树为空,则返回,否则
遍历的技巧
上面的答案是
ABDHIEJCFKG
超详细解题思路
如果二叉树为空,则返回,否则
遍历的技巧
上面的答案是
HDIBEJAFKCG
超详细解答
1秒解题小技巧
想想一根垂直的线在树的左侧
从左向右扫描,扫到谁就输出谁
如果二叉树为空,则返回,否则
上面的答案是
HIDJEBKFGCA
超详细解答
如果二叉树为空,则返回,否则
上面的答案是
ABCDEFGHIJK
这个不用讲了,最简单了!
就是根在不同的位置,进行不同的遍历。
遍历的不同方式决定你如何使用树中的各个结点,我们下节课再见。
这是我的个人学习笔记,主要是应付考研复习使用,充斥着一些吐槽和个人观点,并不严谨,欢迎大家参考、指正。
]]>前面,我们学习了二叉树的五大特性,那么既然说了这么久的二叉树,是时候讲一讲二叉树的数据结构该如何定义了。
如果你忘了之前的内容,点击下面的链接回顾:
数据结构之树-树的存储结构(一)-学习笔记-47
数据结构之树-树的存储结构(二)-学习笔记-47
根据我们之前讨论树的存储结构,我们可以直到,树和结点里面包含的元素可以很灵活,我们甚至可以将链式存储结构跟顺序存储结构结合在一起使用。因为这样表达树这个数据结构才方便。但是
上面的图中是一棵完全二叉树,我们将它存放进一维数组
元素 | A | B | C | D | E | F | G |
---|---|---|---|---|---|---|---|
下标 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
我们发现,使用层序法排列的完全二叉树
我们在看一下这张图,图中并不是完全二叉树,更不是满二叉树
^
,代替即可元素 | A | B | C | D | ^ | F | G |
---|---|---|---|---|---|---|---|
下标 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
还记得斜二叉树嘛?
它的存储可能会是
元素 | A | B | ^ | C | ^ | ^ | ^ |
---|---|---|---|---|---|---|---|
下标 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
这里我们就发现不对了!
在说明链式存储结构前,我们先想象,如果是链式结构
二叉链表
。左子树指针域 | 数据域 | 右子树指针域 |
---|---|---|
Lchild | Data | Rchild |
1 | struct BTNode{ |
如图所示,我们就可以使用链表构建一棵树了,非常简单!
我们之后基本上都是用链式存储结构构建二叉树。
这是我的个人学习笔记,主要是应付考研复习使用,充斥着一些吐槽和个人观点,并不严谨,欢迎大家参考、指正。
]]>我们上节课我们讲了三条性质,从层数、层节点数、深度、总结点数、以及度的概念了解了二叉树的一些有趣的性质,今天我们继续看看,二叉树还有什么有趣的特性。
预习一下取整符号:
具有 n 个结点的完全二叉树
的深度 K 为$\left \lfloor \log_{2}n \right \rfloor+1$
我们再看看满二叉树
如何通过结点数判断深度
还记得完全二叉树的叶子吗?只会出现在最下面两层,所以我们可以推出
完全二叉树
的倒数第二层一定是满二叉树
,完全二叉树倒数第二层的满二叉树的结点数为$n=2^{(k-1)}-1$。上图的计算就是综上,我们知道了
完全二叉树
倒数第二层满二叉树的最大节点数,$n=2^{(k-1)}-1$完全二叉树
深度为 k 的最大结点树为$n=2^{k}-1$完全二叉树
结点 n 的取值范围是:$2^{(k-1)}-1 < n \leq 2^{k}-1$完全二叉树
的深度 K 为$\left \lfloor \log_{2}n \right \rfloor+1$如果有一个 n 个结点的完全二叉树
这是我的个人学习笔记,主要是应付考研复习使用,充斥着一些吐槽和个人观点,并不严谨,欢迎大家参考、指正。
]]>我们介绍了树,介绍了树中使用最多的二叉树,以及一些特殊的二叉树。今天我们就来看看二叉树为什么很好的体现了树的特性,二叉树有哪些有趣的特点呢?
我们先回顾一下度的概念
上面的二叉树中
我们又发现
总节点数-1
总节点数-1
总是等于 $n1+2\times n^{2}$这是我的个人学习笔记,主要是应付考研复习使用,充斥着一些吐槽和个人观点,并不严谨,欢迎大家参考、指正。
]]>我们介绍了什么是二叉树、以及二叉树一些与众不同的特点,例如只能有两个孩子,两个孩子还分为左子树和右子树,那么今天我们来看看特殊的二叉树。
¡¡™
顾名思义,斜树一定要倾斜。也就是说,一棵树
这样就不是斜树了。
如果有一棵树
都存在左子树和右子树
叶子都在同一层上
满二叉树
叶子只能出现在最下一层
,如果最下一层不是叶子,那证明所有叶子并不是在同一层上,不符合满二叉树条件非叶子结点的度一定是 2
,度是每个结点的子树数量,既然是二叉树,不是叶子结点度一定是2在同样深度的二叉树中,满二叉树的结点一定最多
,这基本是废话,层数相同,满二叉树的每个结点都有左子树和右子树,结点一定是满二叉树最多。如果存在一棵树
我们来看看
完全二叉树 | 满二叉树 |
---|---|
你可以这么理解
叶子结点只能出现在最下两层
,因为每个结点的编号是一层一层的写,要满足完全二叉树的所有编号跟满二叉树编号相同,必然不能突破两层,因为超过两层,第三层的哪几个结点的序号,肯定就找不到了。最下层的叶子一定集中在左侧连续位置
,必须的啊,因为新的一层是从左向右编号,也在一定是在左侧连续位置啊,右边还没编号呢…倒数第二层,如果有叶子结点,一定都在右部连续位置
,也是必须的啊,倒数第二层的叶子,肯定是最后一层叶子没占的那些地方啊!如果结点的度为1,那该结点只有左孩子
,也是废话,从左向右编号,结点只有1个孩子,那肯定是左孩子啊….同样结点的二叉树,完全二叉树深度最小
,必须的啊,深度是层数,完全二叉树必须补完一层再来下一层,肯定是完全二叉树深度最小。注意:满二叉树一定是完全二叉树,完全二叉树不一定是满二叉树。
图示 | 原因 |
---|---|
5号结点没有左子树,竟然出现了右子树,错误! | |
6号7号应该是3号的左右子树,第三层没有补全就写了第四层,错误! | |
5号下面的左右子树没有,就直接蹦到后面去了,这不是完全二叉树,错误! |
这是我的个人学习笔记,主要是应付考研复习使用,充斥着一些吐槽和个人观点,并不严谨,欢迎大家参考、指正。
]]>前面,我们从只能表示父母的双亲表示法,升级到了能同时表示双亲和孩子的双亲父母表示法,在学习孩子兄弟表示法之前,我们先来看看二叉树是什么。
树形数据结构有很多种树
,但二叉树是使用范围最广的,也最具有代表意义,所以学习树,我们不能不提二叉树。
对于二叉树(Binary Tree),你应该知道
空二叉树
互不相交
的二叉树组成左子树
和右子树
。这个定义,是一个递归的形式
我们来具体看看什么是二叉树,上面这个图,我们发现
左子树和右子树
没有根也没有结点。
拥有三个结点的普通树
只有以上两种情况
拥有三个结点的二叉树
就会有以上五种情况
切记
二叉树是分左右的!我们下节课讲几种特殊的二叉树。
这是我的个人学习笔记,主要是应付考研复习使用,充斥着一些吐槽和个人观点,并不严谨,欢迎大家参考、指正。
]]>前面,我们介绍了双亲表示法
,每个结点中出了节点的值,还保存了parent
的信息。今天我们换个角度,用孩子表示法
来看看如何构建树的存储结构。
现在双亲表示发的问题在哪?
想达到什么效果?
数组下标 | Data | Pointer | P1 | P1-Node | P2 | P2-Node | P3 | P3-Node |
---|---|---|---|---|---|---|---|---|
0 | A | -> | 1 | -> | 2 | -> | 3 | NULL |
1 | B | -> | 4 | NULL | ||||
2 | C | NULL | ||||||
3 | D | -> | 5 | -> | 6 | NULL | ||
4 | E | -> | 7 | -> | 8 | -> | 9 | NULL |
5 | F | NULL | ||||||
6 | G | -> | 10 | NULL | ||||
7 | H | NULL | ||||||
8 | I | NULL | ||||||
9 | J | NULL | ||||||
10 | K | NULL |
经过修改,我们看这个架构,由数组和链表组合而成
等等…我们不是说最好再有双亲信息
就更完美了吗?
没关系,我们把上一节的双亲表示法和孩子表示法结合组成双亲孩子表示法
数组下标 | Data | Parent | Pointer | P1 | P1-Node | P2 | P2-Node | P3 | P3-Node |
---|---|---|---|---|---|---|---|---|---|
0 | A | -1 | -> | 1 | -> | 2 | -> | 3 | NULL |
1 | B | 0 | -> | 4 | NULL | ||||
2 | C | 0 | NULL | ||||||
3 | D | 0 | -> | 5 | -> | 6 | NULL | ||
4 | E | 1 | -> | 7 | -> | 8 | -> | 9 | NULL |
5 | F | 3 | NULL | ||||||
6 | G | 3 | -> | 10 | NULL | ||||
7 | H | 4 | NULL | ||||||
8 | I | 4 | NULL | ||||||
9 | J | 4 | NULL | ||||||
10 | K | 6 | NULL |
我们将结点的信息再次增加一个 Parent 信息,这样我们既可以直到孩子的信息,又可以直到双亲的信息,并且孩子的后期扩展也不会大量的浪费内存空间。
我们来看看如何构建这个双亲孩子表示法
1 |
|
之后还有孩子兄弟表示法,我们先学习完二叉树的概念,然后再看看孩子兄弟表示法。
这是我的个人学习笔记,主要是应付考研复习使用,充斥着一些吐槽和个人观点,并不严谨,欢迎大家参考、指正。
]]>前面,我们学习了树的一些基础内容,今天我们就来看看,树有哪些存储结构。说到存储结构我们会想到线性表中的顺序存储结构和链式存储结构,那么树会怎么设计自己的存储结构呢?我们一起来看一看~
要存储树,用简单的顺序存储结构和链式存储结构都不行。因为树中有双亲、孩子、兄弟这些概念,我们不能用简单的顺序、链式存储结构表达树中各个结点的情况。所以,根据不同的角度,我们有三种存储结构表示一棵树
双亲表示法,顾名思义,就是以双亲作为索引的关键词的一种存储方式。
1 |
|
数组下标 | data | parent |
---|---|---|
0 | A | -1 |
1 | B | 0 |
2 | E | 1 |
3 | H | 2 |
4 | I | 2 |
5 | J | 2 |
6 | C | 0 |
7 | D | 0 |
8 | F | 7 |
9 | G | 7 |
10 | K | 9 |
上面这就是根为 A 的树在连续内存中存储的情况
结构体 tree 描述了树的情况
结构体 pt 描述了结点的情况
所以,我们在使用双亲表示法的时候,这种存储结构,我们找父母
但是我们在找某个结点的孩子是什么的时候,就不行了,就需要遍历整个树去寻找…
看着这张图,我们改一下结构,让结点中多包含一些信息
数组下标 | data | parent | child-1 | child-2 |
---|---|---|---|---|
0 | A | -1 | 1 | 6 |
1 | B | 0 | 2 | -1 |
2 | E | 1 | 3 | 4 |
3 | H | 2 | -1 | -1 |
4 | I | 2 | -1 | -1 |
5 | J | 2 | -1 | -1 |
6 | C | 0 | -1 | -1 |
7 | D | 0 | 8 | 9 |
8 | F | 7 | -1 | -1 |
9 | G | 7 | 10 | -1 |
10 | K | 9 | -1 | -1 |
我们在原有结点的结构体中,增加了两个孩子结点的信息,这样我们的每个结点就都包括
相应的,我们也可以在结点的结构体信息中包含其他信息
我们看到,对存储结构进行不同的修改,就可以保存不同的信息,相应的,我们可以根据需要定义不同的存储结构,然后保存每个结点你需要的信息。
一个存储结构设计的是否合理,取决于基于该存储结构的运算是否适合、是否方便,时间复杂度好不好等等。
这是我的个人学习笔记,主要是应付考研复习使用,充斥着一些吐槽和个人观点,并不严谨,欢迎大家参考、指正。
]]>