根据二叉树的遍历序列重建这颗树
问题
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树
如果该二叉树的前序序列为: 1, 2, 4, 7, 3, 5, 6, 8
该二叉树的中序序列为: 4, 7, 2, 1, 5, 3, 8, 6
考察要点
二叉树的结构, 二叉树的前序遍历, 中序遍历, 后序遍历和层次遍历
二叉树前序遍历的序列中, 第一个元素就是树的根结点
二叉树后序遍历的序列中, 最后一个元素就是树的根结点
二叉树中序遍历的序列, 一个结点的左子树中的值肯定在该结点左边, 而右子树的值肯定在该结点的右边
思路
前序遍历中的第一个元素就是根结点
根据这个根结点的值, 去中序遍历中查找该值所在位置
在中序遍历中, 该值所在位置的左边就是它的左子树, 右边就是它的右子树
重复1, 2, 3步骤, 进行二叉树的递归重建
代码
二叉树的结点类
class BinaryTreeNode { int value; BinaryTreeNode left; BinaryTreeNode right; }复制代码
准备数据
int[] preList = new int[]{1, 2, 4, 7, 3, 5, 6, 8}; int[] inList = new int[]{4, 7, 2, 1, 5, 3, 8, 6}; // public BinaryTreeNode recreateBinaryTree(int[] preList, int preStart, int preEnd, int [] inList, int inStart, int inEnd) // 传入前序序列, 开始结束索引, 中序序列, 开始结束索引 c.recreateBinaryTree(preList, 0, 7, inList, 0, 7);复制代码
代码实现
public BinaryTreeNode recreateBinaryTree(int[] preList, int preStart, int preEnd, int [] inList, int inStart, int inEnd) { // 进行边界条件的判断 if (preList.length == 0) return null; if (inList.length == 0) return null; if (preStart > preEnd) return null; if (inStart > inEnd) return null; // 打印二叉树的重建日志 System.out.println("~~~~~~~~~~~~~~~~~~~~~~~"); System.out.println("preStart: " + preStart + ", preEnd: " + preEnd); System.out.println("inStart: " + inStart + ", inEnd: " + inEnd); // 前序遍历中的第一个元素就是根结点 int value = preList[preStart]; BinaryTreeNode node = new BinaryTreeNode(); node.value = value; System.out.println("value: " + value); // 从中序遍历中查找该值所在索引 int idx = inStart; for (int i = inStart; i <= inEnd; i++) { if (inList[i] == value) { idx = i; break; } } System.out.println("value: " + value + ", idx: " + idx); // 递归 // 构建左子树 node.left = recreateBinaryTree(preList, preStart + 1, preStart + idx - inStart, inList, inStart, idx - 1); // 构建右子树 node.right = recreateBinaryTree(preList, preStart + idx - inStart + 1, preEnd, inList, idx + 1, inEnd); return node; }复制代码
扩展
二叉树的前序遍历
思路: 先访问该结点本身, 再访问该结点的左子树, 最后访问该结点的右子树
public void inOrder(BinaryTreeNode node) { if (node == null) return; inOrder(node.left); System.out.print(node.value + ""); inOrder(node.right); }复制代码
二叉树的中序遍历
思路: 先访问一个结点的左子树, 再访问该结点本身, 最后访问该结点的右子树
public void inOrder(BinaryTreeNode node) { if (node == null) return; inOrder(node.left); System.out.print(node.value + ""); inOrder(node.right); }复制代码
二叉树的后序遍历
思路: 先访问一个结点的左子树, 再访问该结点的右子树, 最后访问结点本身
public void postOrder(BinaryTreeNode node) { if (node == null) return; postOrder(node.left); postOrder(node.right); System.out.print(node.value + ""); }复制代码
二叉树的层次遍历
思路: 利用队列, 实现二叉树的层次遍历
先将根结点入队
从队列中取出结点, 先访问该结点中的值, 再判断该结点是否有左右子结点, 如果有则入队, 否则不入队
直到队列为空, 则也代表了二叉树已经遍历完毕
public void levelOrder(BinaryTreeNode node) { if (node == null) return; // 创建一个队列 Queue<BinaryTreeNode> list = new java.util.LinkedList<>(); // 将根结点入队 list.add(node); // 如果队列不为空 while (!list.isEmpty()) { // 则从队列中取出一个结点 BinaryTreeNode pollNode = list.poll(); System.out.print(pollNode.value + " "); // 如果结点有左子结点, 则将该结点入队 if (pollNode.left != null) list.offer(pollNode.left); // 如果结点有右子结点, 则将该结点入队 if (pollNode.right != null) list.offer(pollNode.right); } }复制代码
总结
初见这个问题时, 脑海一片空白. 只有静下以来, 充分分析二叉树的各种遍历的特点, 才能找到解决问题的方法.
在解题的过程中, 我们把构建二叉树的大问题, 分解成构建二叉树的左右子树的问题. 然后发现它们的本质是一样的, 因此我们想到了用递归方式解决.
在碰到自己没有头绪的问题时, 我们要躺平, 睡一会儿, 何必要跟自己过不去呢?
作者:云淡风轻的博客
链接:https://juejin.cn/post/7017700865356070925