小知识,大挑战!本文正在参与「程序员必备小知识」创作活动。
本文已参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金。
问题
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树
如果该二叉树的前序序列为: 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);
}
复制代码
二叉树的中序遍历
思路: 先访问一个结点的左子树, 再访问该结点本身, 最后访问该结点的右子树
扫描二维码关注公众号,回复:
13166797 查看本文章
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);
}
}
复制代码
总结
初见这个问题时, 脑海一片空白. 只有静下以来, 充分分析二叉树的各种遍历的特点, 才能找到解决问题的方法.
在解题的过程中, 我们把构建二叉树的大问题, 分解成构建二叉树的左右子树的问题. 然后发现它们的本质是一样的, 因此我们想到了用递归方式解决.
在碰到自己没有头绪的问题时, 我们要躺平, 睡一会儿, 何必要跟自己过不去呢?