您的位置:首页 > 其它

常见算法基础题思路简析(三)-二叉树篇

2017-09-28 22:08 323 查看

常见算法基础题思路简析(三)-二叉树篇

标签: algorithms

常见算法基础题思路简析三-二叉树篇
判断是否为平衡二叉树

判断是否为完全二叉树

找出搜索二叉树中换位的两个节点

打印纸的折痕

二叉树整棵树上节点间最大距离

找到含有节点最多的搜索二叉子树

按层打印二叉树并换行

递归方式实现二叉树的先序中序和后序的遍历

非递归方式实现二叉树的先序中序和后序的遍历
先序遍历

中序遍历

后序遍历

本文对和 二叉树 有关的常见算法基础题思路分类进行分析和总结,并以 Java 为例,适当指出需要注意的编程细节

相关题目和代码在 GitHub: https://github.com/brianway/algorithms-learning

题目见
com.brianway.learning.algorithms.lectures.binarytree


判断是否为平衡二叉树

题目见
CheckBalance


思路:

判断左子树是否为平衡二叉树,是则返回高度,不是返回 -1

判断右子树是否为平衡二叉树,是则返回高度,不是返回 -1

若左右子树均时平衡二叉树,则看两者高度差是否大于 1。是则返回 -1,不是则取两者中大的再加上 1

注意:

这里对返回值意义复用了,非负数则表示高度,负数表示不是平衡二叉树

判断是否为完全二叉树

题目见
CheckCompletion


思路:

使用队列,按层遍历二叉树,分以下几种情况依次判断:

如果只有右孩子,返回 false

如果已经遍历过最后一个非叶子节点,当前节点还是非叶子节点,返回 false

如果左孩子非空,入队

如果右孩子非空,入队;如果右孩子空,表示该节点应为最后一个非叶子节点

若遍历完,则是完全二叉树

找出搜索二叉树中换位的两个节点

题目见
FindErrorNode


思路:

中序遍历二叉树,找到出现降序的节点。第一次出现降序的较大值为换位的两个节点中的较大值,最后一次降序的较小值为换位节点中的较小值

注意:

可能出现一次降序,可能出现两次降序。

若出现一次降序,则这两个值就是换位节点;

若出现两次降序,则第一次降序的较大值和第二次降序的较小值为换位节点

可以用非递归方式中序遍历二叉搜索树来实现

打印纸的折痕

题目见
FoldPaper


思路:

使用一个队列才存结果

每个 fold 里,先递归调用 fold,再将当前折痕方向入队,再递归调用 fold

从队列依次取出折痕即可

注意:

每个 fold 里的前后两次调用,方向相反,一上一下,和折纸方向有关

二叉树整棵树上节点间最大距离

题目见
LongestDistance


思路:

递归的调用 find,每步迭代更新这两个值(当前子树的节点最大距离,到当前子树根结点的最大长度):(MaxLength,MaxToRoot),并将其返回

当前的 MaxToRoot = max(左子树的 MaxToRoot,右子树的 MaxToRoot) + 1

当前的 MaxLength = max(一边的最大距离,两边的最大距离)

一边的最大距离 = max(左子树的 MaxLength,右子树的 MaxLength)

两边的最大距离 = 左子树的 MaxToRoot + 右子树的 MaxToRoot + 1

注意:

需要返回两个值,所以用数组当返回类型

分类/划分标准和思路理清了,代码很简单

找到含有节点最多的搜索二叉子树

题目见
MaxSubtree


思路:

需要记录满足条件搜索二叉树的最大值,最小值,节点个数,记为数组
info[]
,并返回该搜索二叉树的根节点

每一步递归对左孩子和右孩子调用,然后分情况讨论

左孩子的调用结果是左孩子,右孩子的的调用结果是右孩子,且左右孩子和根节点满足大小关系,更新上述
info[]
,返回根节点

不满足上面的条件则从左右调用结果中选择节点个数大的结果(题目要求),更新上述
info[]
,返回调用结果

左右节点个数相等,选择根结点值大的,更新上述
info[]
,返回调用结果

注意:

空节点判断,数组初始化(最大值,最小值,个数0)

更新
info[]
时,尤其对情况一,需要判断左右孩子是否为空的情况

按层打印二叉树并换行

题目见
TreePrinter


思路:

使用队列,先将根节点入队

使用两个指针 last 和 nlast 分别代表当前行的最后一个节点和下一行的最后一个节点,初始值均为 root

每次从队列取出一个节点,并将其左右节点入队,每入队一个,更新 nlast 为入队的节点

判断当前取出的节点是否为 last,若是,则说明这时此行最后一个,故更新 last = nlast

注意:

每出队一个节点,入队其左右孩子,这里 每次 都需要更新 nlast,因为不知道下一行在何时开始没新节点加入

结尾条件需要注意,只有当前节点为 last 且此时队列还非空时,才进行 last 的更新,同时换行,非空这个需要判断,否则会多出一个空行

递归方式实现二叉树的先序、中序和后序的遍历

一定是先左后右,先序、中序和后序指的是根节点遍历的先后

题目见
TreeToSequence


思路:

递归调用即可,终止条件是该节点为 null

不为 null,则将二叉树的“左”,“根”,“右”按遍历顺序的要求打印

注意:

没啥注意的,三四行代码就完了

非递归方式实现二叉树的先序、中序和后序的遍历

题目见
TreeToSequence2


都是使用 来实现的

先序遍历

思路:有点像深度优先遍历

使用一个栈,先将根节点压栈,当栈非空时进行以下步骤

每次出栈一个节点,并先将其右孩子压栈,再将其左孩子压栈

注意:

利用栈“后进先出”的特性,使得右孩子会在左孩子之后出栈

中序遍历

思路:

使用栈,先将根节点入栈,当栈非空时进行以下步骤

当当前节点和其左孩子均不为空时,将其左孩子入栈,当前节点指向左孩子

当当前节点为空或者左孩子为空时,从栈顶取出一个,并将当前节点指向右孩子(可能为空)。若该取出的节点有右孩子,则将右孩子入栈。

注意:

“当前节点左孩子为空则出栈”用于触发左子树到顶的情况

“当前节点为空则出栈”用于触发右孩子为空的回退

后序遍历

思路:核心在于使用最近访问节点 last 来标记是从左孩子还是右孩子遍历完返回的,从而确定是入栈还是出栈

使用栈,先将根节点入栈,当栈非空时进行以下步骤

每次查看栈顶节点(但不出栈),根据栈顶节点的情况分类判断:

左孩子非空且上次访问既不为左孩子也不为右孩子,则左孩子入栈

否则,若右孩子非空且上次访问不为右孩子,则右孩子入栈

否则,表示此时左右孩子均为空或均访问过,栈顶出栈,并记为最近访问节点 last

注意:

按上面的逻辑左孩子出栈后,右孩子才会入栈,所以是先左后右的顺序

右孩子是上次访问的节点时,前两条都不满足,所以一定会是孩子的根结点出栈

作者@brianway更多文章:个人网站
|
CSDN
|
oschina
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  二叉树 算法