前言
二叉树各种花里胡哨的算法题真的把我搞晕了,今天特地整理出一类有关二叉树的算法题,希望能帮助阅读到此文章的人,今后不再受此类题型的困扰。
一、题目类型
已知二叉树的两种遍历序列,请根据该序列构建二叉树;
①根据一棵树的前序遍历与中序遍历构造二叉树。
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
力扣105题
②根据一棵树的中序遍历与后序遍历构造二叉树。
中序遍历 inorder = [9,3,15,20,7]
后序遍历 postorder = [9,15,7,20,3]
力扣106题
③根据一棵树的前序遍历与后序遍历构造二叉树。
前序遍历 inorder = [1,2,4,5,3,6,7]
后序遍历 postorder = [4,5,2,6,7,3,1]
力扣889题
二、题型规律
规律啥的基本上大家都看的出来,无非就是根据手握两种遍历序列,去构建二叉树。
这题的变化点个人感觉就是给你的遍历序列,可能是字符串,可能是数组,也可能是集合,那么中间就还存在一个把序列给转换成方便我们使用的状态,例如字符串通过正则分割,然后转换为数组啥的(本文章的遍历序列是数组形式),过分点就是二叉树的结点类也要你自己写。那么节点类就这么写↓
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) { val = x; }
}
三、解题套路
1.明确序列分工(结点构建序列,左右子结点判断序列)
题目给了两个序列,我们得先明确,两个序列分别做什么工作。
第一,构建一棵二叉树一般都是从根节点开始往下构建子树(我目前没见过从子树开始往上构建到根节点的情况,但不知道有没有,话不敢说绝对,嘿嘿),既然要从根节点开始往下构建,那我们就需要一个能从根节点开始往下读取子节点的序列,符合这个要求的序列有前序遍历和后序遍历(后序遍历你倒着来看,就相当于一个改版的前序遍历:根,右,左);
第二,我们还需要一个序列,来确定我们读取的结点到底是左结点还是右结点。此时我们就需要中序遍历,如果没有中序遍历,就用后续遍历;
总结:结点构建序列:前序、后序;左右子结点判断序列:中序、后序;
2.构建结点下标查询表
将左右子结点判断序列每个节点存入哈希表中,方便我们查询下标
3.根据结点构建序列逐个构建结点(递归实现)
然后按照结点构建序列逐个构建结点,通过左右子节点判断序列判断当前构建的结点属于左结点还是右结点;
四、代码实现
①根据一棵树的前序遍历与中序遍历构造二叉树。
class Solution {
Map<Integer, Integer> map = new HashMap<>();
int index = 0;
public TreeNode buildTree(int[] preorder, int[] inorder) {
for(int i = 0; i < inorder.length; i++){
map.put(inorder[i], i);
}
return build(0, preorder.length - 1, preorder);
}
public TreeNode build(int left, int right, int[] preorder){
if(left > right) return null;
int mid = map.get(preorder[index]);
TreeNode root = new TreeNode(preorder[index]);
index++;
root.left = build(left, mid - 1, preorder);
root.right = build(mid + 1, right, preorder);
return root;
}
}
②根据一棵树的中序遍历与后序遍历构造二叉树。
class Solution {
Map<Integer,Integer> map = new HashMap<>();
int index = 0;
public TreeNode buildTree(int[] inorder, int[] postorder) {
for(int i = 0; i < inorder.length; i++){
map.put(inorder[i], i);
}
index = postorder.length - 1;
return build(0, postorder.length - 1, postorder);
}
public TreeNode build(int left, int right, int[] postorder){
if(left > right) return null;
int mid = map.get(postorder[index]);
TreeNode root = new TreeNode(postorder[index]);
index--;
root.left = build(left, mid - 1, postorder);
root.right = build(mid + 1, right, postorder);
return root;
}
}
③根据一棵树的前序遍历与后序遍历构造二叉树。
class Solution {
Map<Integer, Integer> map = new HashMap<>();
int index = 0;
public TreeNode constructFromPrePost(int[] preorder, int[] postorder) {
for(int i = 0; i < postorder.length; i++){
map.put(postorder[i], i);
}
return build(0, postorder.length - 1, preorder);
}
public TreeNode build(int left, int right, int[] preorder){
if(left > right || index >= preorder.length) return null;
TreeNode root = new TreeNode(preorder[index]);
index++;
if(index < preorder.length && left < right){
int mid = map.get(preorder[index]);
root.left = build(left, mid, preorder);
root.right = build(mid + 1, right - 1, preorder);
}
return root;
}
}
总结
观察三种题型的解题代码,其实有很多相同之处,唯独后序遍历的递归算法有稍微一些变形,这三个代码可以一起理解,勤加练习,这类题型问题就不大了