二叉树
题目一
二叉树节点结构
public class Node<T> {
T value;
Node left;
Node right;
}
注意:孩子节点仅有一个父节点,不能形成环。
用递归和非递归两种方式实现二叉树的先序、中序和后序遍历。
如何直观地打印一棵二叉树?
如何完成二叉树的宽度优先遍历(常见题目:求一棵二叉树的宽度)?
二叉树遍历的递归序
通过递归的方式遍历二叉树,把遍历过程展开得到的顺序为递归序。
public class TravesalRecur {
public void travelRecur(Node head) {
if (null == head) {
return;
}
System.out.print(head.value);
System.out.print(",");
travelRecur(head.left);
System.out.print(head.value);
System.out.print(",");
travelRecur(head.right);
System.out.print(head.value);
System.out.print(",");
}
}
用递归方式实现二叉树的先序、中序和后序遍历
通过递归序实现先序、中序和后序遍历。
通过二叉树的递归序可以很容易地得出二叉树的先序、中序和后序遍历的实现方法。
用非递归方式实现二叉树的先序、中序和后序遍历
递归使用线程栈,大小有限制,递归层次太深,栈会溢出。
解决方案:
(1)限制递归深度;
(2)递归改写为非递归,利用栈结构,从内存分配空间。
非递归先序遍历
利用栈实现,压栈顺序为:头右左,出栈顺序为:头左右。
(1)头结点压栈;
(2)弹栈,打印节点;
(3)有右压栈,有左压栈,先右后左;
(4)循环执行(2),(3),直到栈为空。
非递归中序遍历
利用栈实现,压栈顺序为: 头左右 出栈顺序:左头右。
(1)从头结点开始;
(2)把每个节点的整棵树的左边界压栈;
(3)依次弹栈,打印;
(4)对每个弹出的节点,如果有右孩子,对右孩子重复执行(2),(3),直到栈为空。
非递归后序遍历
用1个栈的实现方式很难。
利用两个栈实现,1个栈,1个收集栈,压栈顺序为:头左右,出栈顺序为:头右左。
(1)头结点压栈;
(2)弹栈,压收集栈;
(3)有左压栈,有右压栈,先左后右;
(4)循环执行(2),(3),直到栈为空;
(5)依次弹出收集栈,打印。
如何完成二叉树的宽度优先遍历?
二叉树的深度优先遍历:和先序遍历一样。
利用队列完成,进队列的顺序:从上到下,从左到右。
(1)头结点进队列;
(2)出队列,打印;
(3)有左进队列,有右进队列;
(4)循环执行(2),(3),直到队列为空。
求一棵二叉树的宽度
实现一:利用Map保存每个节点所在的层级,对二叉树做宽度优先遍历计算出宽度。
实现二:利用几个变量,对二叉树做宽度优先遍历计算出宽度。
题目二
二叉树的相关概念及其实现方式
如何判断一棵二叉树是搜索二叉树?
如何判断一棵二叉树是完全二叉树?
如何判断一棵二叉树是满二叉树?
如何判断一棵二叉树是平衡二叉树?
二叉树题目套路(二叉树递归套路,树型DP)
搜索二叉树:整棵树和所有子树都满足,根节点比左子树的所有节点大,比右子树的所有节点小。中序遍历得到的序列按照升序排列。
完全二叉树:按照从上到下,从左到右的顺序填满节点的二叉树。只有最下边一层节点可以不填满,但是必须是按照从左到右的顺序填满,中间不能有空缺。
满二叉树:每一层的节点都是填满的,高度(h)和节点数(n)满足公式:n = 2^h - 1
平衡二叉树:整棵树和所有子树都满足,根节点的左子树高度与右子树高度相差不超过1。
二叉树题目套路:二叉树递归套路,解决树型DP问题,并不能解决所有二叉树的问题,需要具体问题具体分析。
(1)向左子树要信息;
(2)向右子树要信息;
(3)结合自身、左子树信息、右子树信息,根据约束条件计算出最终的结果。
注意:面试时二叉树相关的问题可以优先考虑。
如何判断一棵二叉树是搜索二叉树?
实现一:按照中序遍历,检查升序。
实现二:递归检查。
从根节点开始,对每个节点进行处理:
(1)向左子树获取搜索二叉树标志、最大值、最小值;
(2)向右子树获取搜索二叉树标志、最大值、最小值;
(3)然后检查条件:左子树、右子树都是搜索二叉树,并且根节点比左子树的最大值小、比右子树的最小值大,只要不满足就不是搜索二叉树。加工出本节点的搜索二叉树标志、最大值、最小值。
整棵树的每个节点会在递归检查过程中都会检查一遍,每个节点满足该检查条件则是搜索二叉树。
如何判断一棵二叉树是完全二叉树?
按照宽度优先遍历,检查:
(1)如果一个节点有右孩子,无左孩子,则不是完全二叉树;
(2)在(1)不违规的条件下,遇到第一个左右孩子不双全时,后续所有的节点必须是叶节点。
完全二叉树的所有子树并不都是完全二叉树,不能使用递归套路解决。
如何判断一棵二叉树是满二叉树?
实现一:获取高度h,获取节点个数n,如果满足公式:n = 2^h -1,则是满二叉树。
实现二:使用递归套路实现。从根节点开始,对每个节点进行处理:
(1)向左子树获取高度、节点个数;
(2)向右子树获取高度、节点个数;
(3)加工出本节点的高度、节点个数;
(4)通过以上递归处理过程获取整棵树的高度(h)、节点个数(n),如果满足公式:n = 2^h -1,则是满二叉树。
如何判断一棵二叉树是平衡二叉树?
使用递归套路实现。
从根节点开始,对每个节点进行处理:
(1)向左子树获取平衡二叉树标志、高度;
(2)向右子树获取平衡二叉树标志、高度;
(3)然后检查条件:左右子树的高度差不超过1。加工出本节点的平衡二叉树标志、高度。
题目三
给定两个二叉树的节点node1和node2,找到他们的最低公共祖先(LCA)。
实现一:(1)遍历二叉树利用HashMap把每个节点的父节点关系创建出来;
(2)利用整棵树的父节点关系把node1节点的父节点链创建出来;
(3)从node2节点还是往上(整棵树的跟节点)找,每获得一个父节点,检查该节点在不在node1的父节点链中,如果在该节点就是最低公共祖先。
实现二:类似树型DP,递归套路
所有可能的情况为:(1)node1是node2的LCA,或者node2是node1的LCA;
(2)node1和node2不互为LCA。
题目四
在二叉树的中序遍历的序列中,一个节点的下一个节点叫作它的后继节点。
在二叉树中找到一个节点的后继节点。有一棵新的二叉树的节点类型如下,每个节点parent指针指向父节点,头结点的parent为null。
public class Node { public int value; public Node left; public Node right; public Node parent;}
给定一个节点node,请找到它的后继节点。
实现一:按照中序遍历获得序列,然后找节点的后继节点。时间复杂度O(n),空间复杂度O(n)。
实现二:时间复杂度O(logn),空间复杂度O(1)。
(1)节点有右子树,后继为右子树的最左节点;
(2)节点无右子树,往上找父节点,找到的第一个是左孩子的父节点为后继节点;对于整棵树最右的节点,后继节点为null。
题目五
二叉树的序列化与反序列化
如何判断一棵二叉树是另一棵二叉树的子树
题目六
折纸问题
请把一段纸条竖着放在桌子上,然后从纸条的下边向上方对折1次,压出折痕后展开。此时折痕是凹下去的,即折痕突起的方向指向纸条的背面。如果从纸条的下边向上方连续对折2次,压出折痕后展开,此时有三条折痕,从上到下依次是下折痕、下折痕、上折痕。给定一个输入参数N,代表纸条都从下往上连续对折N次。请从上到下打印出所有折痕的方向。
例如:N=1时,打印:down。N=2时,打印:down down up
经分析可得出,折痕的分布对应一棵满二叉树,这棵二叉树的头结点是下折痕,每棵左子树的头结点是下折痕,每棵右子树的头结点是上折痕。从上到下折痕的顺序对应这棵满二叉树的中序遍历顺序。
实现一:空间复杂度O(2^N)。
(1)按照以上结论生成一棵高度N的二叉树;
(2)按照中序遍历打印。
实现方式二:空间复杂度O(N)。不生成完整的二叉树,使用递归套路实现。
参考资料
《bilibili左程云算法课堂》
源码地址
https://gitee.com/easyfun/algorithm-learning/tree/master/binary-tree