二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》:这其实是图的宽度优先搜索的应用。

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

比如这棵树按层遍历的结果为:1 2 3 4 5 6 7 8  也就是一层一层按从左到右的顺序打印,这种遍历的方式是用我们熟悉的队列来实现的,但是在面试中,往往要求面试者在按层打印的时候连同行号相关的信息也打印出来。

案例二:

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

这道题的难点是如何该换行?

其实只需要2个变量,last和nlast,就可以解决这个问题。

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

假设每一层都做从左到右的宽度优先遍历,如果发现遍历到last节点,说明该换行了,换行之后要令last=nlast就可以继续下一行的打印过程,一直重复这个动作,知道所有的节点都打印完了,那么整个过程就可以结束了。那么问题就变成了如何去正确的更新last和nlast,其实只需要让nlast一直跟踪记录宽度优先队列中最新加入的节点即可,这是因为最新加入的队列的节点一定是目前发现的下一行的最右节点,所有在当前行打完时,nlast一定下一行所有节点中最右的节点。

接下来还是用例子来说明整个过程,先准备好一个队列,即为queue,再准备2个变量last和nlast:

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

开始时先令last等于节点1

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

然后把节点1放入queue:

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

从queue中弹出节点1并打印:

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

然后把节点1的孩子,依次放入到queue中,然后放入节点2时,

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

令nlast等于节点2,放入queue中:

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

放入节点3时,令nlast=3,

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

此时发现,弹出的节点1与last相等,所以该换行了,

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

并令last等于nlast,也就是节点3,从queue弹出节点2并打印,

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

然后把节点2的孩子也就是节点4放入queue

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

同时令nlast等于节点4:

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

从queue中弹出节点3并打印:

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

然后把节点3 的孩子放入queue中,放入节点5时:

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

nlast更新为节点5,放入节点6时nlast更新为节点6:

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

此时发现弹出的节点3已经是last节点了,所以换行:

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

并再令last等于nlast,也就是last变成了节点6,last又成功的更新成了下一行最右的节点,

也就是说,nlast始终记录着刚进入队列的节点,也就是一直记录着目前出现的下一层的最右的节点,当某一节点走到last的时候,说明这一层打印完了,需要打印换行,接下来让last等于nlast,就是正确的找到了下一层的最右的节点,从而每次都能正确的完成换行这个动作。

 

 

接下来讲:

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

我们知道平时在程序里面构造的二叉树是存在在内存中,逻辑结构可以使用纸和笔画出来,但是很多时候我们还是希望有用文件的方式把二叉树记录下来,这样一来,下次想要重构二叉树时,就可以凭借在文件中的记录将整颗二叉树还原出来。把二叉树记录成文件的过程叫做二叉树的序列化过程。也叫二叉树的持久化过程。把文件中的记录还原成二叉树的过程叫做二叉树的反序列过程。

二叉树的序列化方法有很多,常见的有先序,中序,后序遍历来序列化。还有根据二叉树按层遍历的方法来进行序列化:

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

 

案例三:

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

二叉树的序列化可以通过先序,中序,后序的方法来序列化。

这里介绍先序的方式(中序和后序的方式与先序是同理的):

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

#表示这个节点为空,节点值不存在。

当然也可以使用其他的特殊字符来表示空的这个节点。

!表示一个值的结束。

也是一个特殊字符,当然也可以使用其他的特殊字符来表示一个值的结束。

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

比如:现在看到的,根据先序遍历的过程,首先遇到的就是头节点12,

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

所以str变为:《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

然后遇到节点3,则在末尾加上《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

变为:《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

然后遇到3的左孩子和右孩子都是空节点,所以继续在str的后面加上《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》 ,变成:

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》,然后遍历12的右孩子,也是空节点,所以str最终为:

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》.

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

为什么在一个节点值的后面要加上一个!或者其他的特殊字符来表示一个值的结束呢?

因为如果不标记一个值的结束,最后产生的结果可能会有歧义。

比如:

这2棵树虽然是不同的树,但是如果一个值没有结束符!号的话,

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

这2颗树的先序序列化的结果都将是123###:

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

 

 

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

接下来介绍如何通过先序遍历序列化的结果字符串str来重构出原来的二叉树的过程,也就是反序列化的过程。把结果字符串str先变成字符串类型的数组,假设记为values,数组代表一颗二叉树先序遍历的节点顺序,比如:《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》,生成的vlaues为:

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》,然后按照先序遍历的顺序建立出整棵树:

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

 

首先遇到字符串1,2,生成的节点值为《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》的节点:

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

然后用剩下的字符串建立节点12的左子树,接下来遇到字符串3,生成节点值为《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》的节点,它是节点12的左孩子:

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

然后用剩下的字符串建立节点3的左子树,遇到#,生成空节点,它是节点3的左孩子:

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

该节点为空,所以这个节点没有后续建立子树的过程了,则回到节点3,用剩下的字符串建立节点3的右子树,接下来又遇到字符串#,生成空节点,它是节点3的右孩子:

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

该节点为空,所以这个节点又没有后续建立子树的过程,那么回到节点3,再回到节点1,用剩下的字符串建立节点1 的右子树,又遇到了字符串#,生成空节点,

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

它是节点1 的右孩子,该节点为空,字符串也用完了,那么整个反序列化的过程结束。

那么可以看到我们用什么样的遍历方式序列化,就选择用什么样的方式反序列化。

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

代码实现也比较容易.

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

一棵树序列化的结果在下次反序列化的时候一定不会发生歧义。一定能够重构出结构与之前完全一样的树。

中序,后序的反序列化过程同理,这里不再详述。

 

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

二叉树还可以用按层的方式进行序列化,也就是按队列依次按层遍历所有的二叉树的节点,遇到空,就在字符串后面添加特殊字符表示空节点,并添加特殊字符表示序列化这个空节点的过程结束了,也就是刚才提到的!或者其他的特殊字符都好。遇到飞控节点就在后面添加节点的值表示该节点序列化的结果,并且也添加特殊字符,表示这个节点的序列化已经完毕了。反序列化过程同理。根据字符串数组并按照层遍历的方式建立出整棵树即可。

 

案例三:

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

旋转词是指一个字符串左边任意长度的子串,挪到右边都叫做这个字符串的旋转词,比如字符串1234它的旋转词包括1234,2341,3412,4123.

如果str1的长度为n,本题最优解可以做到时间复杂度为O(N)

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

那么它是怎么做的呢?首先判断str1与str2长度是否相等,如果不等,可以直接返回flase,

因为2个字符串互为旋转词的话,它的长度应该是相等的,如果str1与str2是相等的话就继续进行后续的判断,首先生成str1+str1这样大的字符串,然后在大字符串中寻找它是否包含str2这个字符串,如果包含str2这个字符串,说明str1和str2是互为旋转词的。如果这个大字符串中不包含这个str2的话,说明str1和str2之间不是互为旋转词的。那么这个寻找的过程实际上就是KMP算法的主要内容。

用KMP算法来做这个匹配即可。

那么这么做为什么是对的?

举例说明:

假设:《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》,那么生成的大字符串就是

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》,我们需要注意的是在这个大字符串中,

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》,1234是str1的旋转词,《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》,2341是这个str1的旋转词,

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》3412是这个str1的旋转词,《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》4123也是这个str1的旋转词。也就是说在str1+str1中:

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

str1+str1这个大字符串穷举了所有的旋转词,如果str2的疮毒等于n,在str1+str1中能够找到str2的话,说明str2一定是str1的旋转词。

 

案例四:

《二叉树按层遍历 基于图的宽度优先搜索的应用 二叉树的序列化和反序列化》

给定一个字符串str,String代表一个句子,将句子中所有的单词逆序。

这个实际上代表了字符串问题在面试中常规的情况,不包含高深的算法,仅仅是考察代码实现能力的题目。

它的做法是之前实现一个。。。

未完待续。。。。。。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

    原文作者:数据结构之图
    原文地址: https://blog.csdn.net/weixin_36836847/article/details/84945608
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞