《剑指offer》 之二叉树
-
二叉树类型为常考题型:
- 结合队列、栈、链表、字符串等数据结构;
- 需要掌握图的基本遍历方式,例如BFS和DFS。
- 需要掌握递归函数的使用,并自己涉及出递归过程。
- 与实际工作结合紧密。
-
遍历方式:
-
前序遍历:根节点->左子树->右子树
-
中序遍历:左子树->根节点->右子树
-
后序遍历:左子树->右子树->根节点
-
4、重建二叉树(二叉树)
-
题目描述:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
-
思路:我们首先根据前序遍历的第一个数字创建根结点,接下来在中序遍历中找到根结点的位置,这样就能确定左、右子树结点的数量;然后分别将左右子树递归划分即可。
-
\(\color{red}{代码实现}\):重建二叉树
17、树的子结构(27ms,5836k)
-
题目描述:
输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
-
思路:
要查找A中是否存在和树B结构一样的子树,分为以下两步:
1. 第一步:在树A中找到和树B的根结点的值一样的结点R; 2. 第二步:判断树A中以R为根结点的子树是否包含和B一样的结构。
遍历结点下面的子树,直到树B中的所有结点在树A中某个子树中找到。
-
\(\color{red}{代码实现}\):树的子结构
18、二叉树的镜像
- 题目描述:操作给定的二叉树,将其变换为源二叉树的镜像。
- 思路:
- 二叉树的镜像:即根结点和每个中间结点的左右子树交换位置得到的一棵二叉树。
- 求镜像的过程:先前序遍历这棵树的每个结点,如果遍历的结点有子结点,就交换它的两个子结点。当交换完所有非叶结点的左、右子结点之后,就得到了树的镜像。
- \(\color{red}{代码实现}\):二叉树的镜像
22、从上往下打印二叉树
- 题目描述:从上往下打印出二叉树的每个节点,同层节点从左至右打印。
- 思路:
此题属于广度优先层次遍历,利用一个队列来实现。层次遍历的基本过程是:- 从根结点出发(依次加入到队列中),并且从队列中取出一个元素;
- 访问该元素所指的结点(即取出该结点的值);
- 若该元素所指结点的左、右孩子结点非空,则将其左、右孩子的指针顺序入队列。
- \(\color{red}{代码实现}\):从上往下打印二叉树
24、二叉树中和为某一值的路径
-
题目描述:输入一颗二叉树的根节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)
-
思路:由于路径是从根结点出发到叶子结点,也就是数路径总是以根结点为起始点,因此我们需要用前序遍历才能首先访问到根结点。具体过程如下:当用前序遍历的方式访问到某一结点时,我们把该结点添加到路径商,并累加该结点的值;如果该结点为叶结点,并且路径中结点值的和刚好等于输入的整数,则当前路径符合要求;如果当前结点不是叶结点,则继续访问它的子节点;当前结点访问结束后,递归函数将自动回到它的父结点。
-
\(\color{red}{代码实现}\):二叉树中和为某一值的路径
38、二叉树的深度
- 题目描述:输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
- 思路:如果一个树只有一个根结点,那它的深度为1;如果根结点只有左子树没有右子树,则在它的左子树深度上加1;同样,如果根结点只有右子树没有左子树,则在它的右子树深度上加1;如果根结点既有左子树又有右子树,则该树的深度为左右子树深度的较大值加1.最后将左右子树递归判断其是否右左右子树(即不断计算左右子树的深度)
- \(\color{red}{代码实现}\):二叉树的深度
39、平衡二叉树
- 题目描述:输入一棵二叉树,判断该二叉树是否是平衡二叉树。
- 思路:
- 如果某二叉树中任意结点的左、右子树的深度相差不超过1(包括根结点和中间结点),那么它就是一棵平衡二叉树。
- 判断每一层结点的的子树深度,但凡不满足左右子树深度差小于等于1,则不是平衡二叉树,相反则为平衡二叉树。
- \(\color{red}{代码实现}\):平衡二叉树
57、二叉树的下一个结点
-
题目描述:给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。(一般是不存在指向父结点的指针)
-
核心:四种可能性:(1)有右子树;(2)无右子树,是父结点的左结点;(3)无右子树,是负结点的有结点;(4)根结点
-
思路:(1)如果一个结点有右子树,那么它的下一个结点就是它的右子树中的最左子节点。从右子节点出发,一直沿着指向左子节点的指针,我们就能找到它的下一个结点。(2)如果一个结点没有右子树,并且该结点是其父结点的左子节点时,那么它的下一个结点就是其父结点。(3)如果一个结点没有右子树,并且该结点还是其父结点的右子节点时,我们沿着指向父结点的指针一直向上遍历,直到找到一个是它父结点地左子节点的结点。
-
\(\color{red}{代码实现}\):二叉树的下一个结点
58、对称的二叉树
- 题目描述:请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。
- 思路:首先判断二叉树A和复制二叉树B是否同时为空,如果两个二叉树都不为空,就判断根结点值是否相等,然后判断A的左子树是否和B的右子树相同(即镜像对称),同时还满足A的右子树和B的左子树相同,是则对称,否则不对称。
- \(\color{red}{代码实现}\):对称的二叉树
59、按之字形顺序打印二叉树
-
题目描述:请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
-
思路:由于需要按照之字形打印,因此属于逐层打印,而每两层之间的打印顺序是反着的,因此需要建立两个栈,交替将当前层的结点打印出来,下一层的结点压入栈中,直到当前层的结点全部打印完毕,那么开始打印当前层的下一层。依次循环,直到两个栈都为空。
-
\(\color{red}{代码实现}\):按之字形顺序打印二叉树
60、把二叉树打印成多行
-
题目描述:从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。(和59题类似)
-
思路:
-
代码实现:
61、序列化二叉树
-
题目描述:请实现两个函数,分别用来序列化和反序列化二叉树
-
二叉树的序列化是指:把一棵二叉树按照某种遍历方式的结果以某种格式保存为字符串,从而使得内存中建立起来的二叉树可以持久保存。序列化可以基于先序、中序、后序、层序的二叉树遍历方式来进行修改,序列化的结果是一个字符串,序列化时通过 某种符号表示空节点(#),以 ! 表示一个结点值的结束(value!)。
-
二叉树的反序列化是指:根据某种遍历顺序得到的序列化字符串结果str,重构二叉树。
-
-
思路:
-
代码实现:
参考:
-
《剑指offer》宝典神功