目录
前言
构建二叉树的前提:
为什么需要两个不同类型的遍历:
前序遍历 + 中序遍历
我们的算法思路如下:
举例:
代码实现
后序遍历 + 中序遍历
结尾
前言
入门数据结构JAVA DS——二叉树的介绍 (构建,性质,基本操作等) (1)-CSDN博客
在上一篇博客中,笔者讲过,如果告诉你遍历顺序,是可以通过算法构建出二叉树的,这篇博客就是补充,笔者想具体的阐述一下 "告诉遍历顺序从而构建二叉树" 算法.
构建二叉树的前提:
要唯一地构建出一棵二叉树,至少需要两个不同类型的遍历结果。最常见的组合是:
- 前序遍历 + 中序遍历
- 后序遍历 + 中序遍历
只用一种遍历顺序无法唯一地确定一棵二叉树,因为同样的遍历顺序可能对应多个不同的二叉树。下面详细说明这些组合是如何帮助构建二叉树的。
为什么需要两个不同类型的遍历:
-
中序遍历:中序遍历能够提供节点在二叉树中相对于左右子树的相对位置,但无法告诉你树的层次结构(即节点的父子关系)。例如,如果只知道中序遍历
D B E A F C
,我们只能得知顺序关系,但无法区分每个节点的父节点和子节点。 -
前序遍历 :前序遍历能够提供根节点和子树的先后顺序。它告诉我们哪个节点是根节点,但无法精确地告诉我们左子树和右子树的细节。
-
后序遍历 :后序遍历提供了叶子节点的顺序以及根节点的最终位置,但同样无法给出左右子树的确切划分。
通俗地说:
- 中序遍历告诉我们每个节点的左右子树。
- 前序遍历或后序遍历帮助我们找到子根节点。
举个例子:
如果有前序遍历 A B D E C F
和中序遍历 D B E A F C
,通过中序遍历可以知道:
A
是根节点(因为前序遍历的第一个是根节点)。- 在中序遍历中,
A
左边的是D B E
,所以这是左子树;A
右边的是F C
,所以这是右子树。
当然,如果告诉你那些位置是空的话,只要一种遍历就够了,可以参考
入门数据结构JAVADS——部分常见的二叉树OJ题目(1) 持续更新-CSDN博客 第四题
前序遍历 + 中序遍历
笔者通过题目来讲解
105. 从前序与中序遍历序列构造二叉树 - 力扣(LeetCode)
我们的算法思路如下:
确定根节点:
- 前序遍历的第一个元素一定是根节点。根据这个根节点,我们可以在中序遍历中找到根节点的位置,从而确定左右子树。
划分左右子树:
- 在中序遍历中,根节点左边的元素都是左子树的节点,右边的元素都是右子树的节点。
- 根据中序遍历的左右划分,再利用前序遍历来确定左右子树的结构。
递归处理左右子树:
- 对左子树和右子树分别递归重复上述步骤,直到构建完成。
举例:
我们有如下的前序遍历和中序遍历:
- 前序遍历:
[A, B, D, E, C, F]
- 中序遍历:
[D, B, E, A, F, C]
步骤 1:确定根节点
- 根据前序遍历的第一个元素,
A
是根节点。 - 在中序遍历中找到
A
,可以划分为:- 左子树的中序遍历:
[D, B, E]
- 右子树的中序遍历:
[F, C]
- 左子树的中序遍历:
步骤 2:处理左子树
- 前序遍历的第二个元素
B
是左子树的根节点。 - 在左子树的中序遍历
[D, B, E]
中找到B
,可以划分为:- 左子树的左子树的中序遍历:
[D]
- 左子树的右子树的中序遍历:
[E]
- 左子树的左子树的中序遍历:
步骤 3:处理右子树
- 前序遍历中,接下来的是
C
,这是右子树的根节点。 - 在右子树的中序遍历
[F, C]
中找到C
,可以划分为:- 左子树的中序遍历:
[F]
(即C
的左子树) - 没有右子树。
- 左子树的中序遍历:
这样处理下来,最后的结构是
A/ \B C/ \ /D E F
代码实现
我们用这道力扣上的题目来做代码实现
代码如下
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*/
class Solution {public int preindex=0;public TreeNode buildTree(int[] preorder, int[] inorder) {return buildTreeChilde(preorder,inorder,0,inorder.length-1);}// 大体思路,通过中序找子树,通过前序构建每个结点.public TreeNode buildTreeChilde(int[] preorder, int[] inorder,int inbegin,int inend)// begin 和 end 负责检查是否还有结点{if(inbegin>inend){return null ;}TreeNode root = new TreeNode(preorder[preindex]); int rootidx = find(inorder,inbegin,inend,preorder[preindex]);preindex++;root.left = buildTreeChilde( preorder, inorder, inbegin, rootidx-1);root.right = buildTreeChilde( preorder, inorder, rootidx+1, inend);return root;}private int find(int [] inorder,int begin,int end,int key){for(int i=0;i<inorder.length;i++){if(inorder[i]==key){return i;}}return -1;}
}
可以看到,我们设置了 inbegin , inend,rootidx 这三个变量,它们有什么用呢?
rootidx很简单,每个前序遍历的结点都在中序遍历中有对应位置,rootidx负责定位它,从而将左右子树分割开.
inbegin , inend呢?它们负责确定(子)根结点的左右孩子结点是否是空结点,我们可以看到,每次分割完成以后,对于左右子树来说,如果 inbegin<=inend ,说明还存在结点.如果 inbegin>inend , 那说明已经没有结点了,这很好理解,中序遍历表中 inbegin 已经大于 inend 了, 已经没有结点了.
至于为什么先左后右,则是通过前序遍历的顺序得到的.
通过我们这样的递归,最后就可以构建出二叉树
后序遍历 + 中序遍历
106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode)
也差不多是同理,我们如果倒着去看后序遍历表,就可以发现,差不多是 根 -- 右 -- 左 版本的前序遍历,只要稍微改一下代码就好了
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*/
class Solution {// 左 右 根public int postordernum=0;public TreeNode buildTree(int[] inorder, int[] postorder){postordernum=postorder.length-1;return buildTreeChild(inorder,postorder,0,inorder.length-1);}public TreeNode buildTreeChild(int[] inorder, int[] postorder,int inbegin,int inend){if(inbegin>inend){return null;}TreeNode root=new TreeNode(postorder[postordernum]);int rootindex=find(inorder,inbegin,inend,postorder[postordernum]);postordernum--;root.right=buildTreeChild(inorder,postorder,rootindex+1,inend);root.left=buildTreeChild(inorder,postorder,inbegin,rootindex-1);return root;}private int find(int [] inorder,int inbegin,int inend,int key){for(int i=inbegin;i<=inend;i++){if(inorder[i]==key){return i;}}return -1;}
}
从结尾开始反向遍历后序遍历表,然后去构建, 思路基本是一致的.
结尾
笔者是懒狗,创作不易,知识简单,但毕竟用爱发电!