前序遍历/中序遍历构造二叉树
根据一棵树的前序遍历与中序遍历构造二叉树。
注意:
你可以假设树中没有重复的元素。
题目来源:https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/
基本思路:
1、我们都知道,前序遍历首先遍历出的是父节点,然后是它的左子节树,最后是右子树,因此前序遍历得到的数组中第一个数是二叉树的根节点
2、知道根节点之后,在中序遍历得到数组中查找根节点对应的下标,从而将中序数组分成两个子数组,左边是根节点的左子树,右边是根节点的右子树,并且有根节点的下标,从而可以知道左子树有多少个节点,这里通过求左子树中的节点个数,从而可以更好地在前序数组中找到对应的范围。
3、同样的道理,将对左子树、右子树进行相同的操作,在前序遍历中的数组中找到它的根节点,然后再这个中序遍历的子数组中找到对应的下标,再次划分左右子数组,直到前序遍历的数组已经遍历完,二叉树构造完成.
过程分析:
节点类:
public class Node {
public int value;
public Node left;
public Node right;
public Node(int value){
this.value = value;
}
@Override
public String toString() {
return value+" ";
}
}
测试类:
import java.util.HashMap;
import java.util.Map;
/**
* 根据中序数组、前序数组,从而构建一个二叉树,基本思路:
* 1、由于前序遍历:首先遍历自己,然后遍历它的左子节点,最后遍历它的右子节点,因此
* 前序数组的第一个必然是一个根节点
* 2、知道根节点之后,在中序遍历得到的数组中找到根节点所在的下标,从而将中序数组分成两个
* 子数组,其中左边是根节点的左子树,右边是根节点的右子树
* 3、对每一个子数组都进行重复上面的操作(及通过递归),从而可以构建一棵二叉树
* 当然,在中序遍历得到的数组中找根节点的下标,可以通过for循环来查找,但是相对耗时,
* 因此这里可以使用哈希表进行优化
*/
public class ArrayToTree2 {
public static int[] inOrder = {4,5,8,10,12,15,18};//中序遍历得到的数组
public static int[] preOrder = {10,5,4,8,15,12,18};//前序遍历得到的数组
//定义一个哈希表,从而存放中序数组的值及下表,并且值作为哈希表键,下标作为哈希表的值
public static Map<Integer,Integer> map = new HashMap<>();
public static void main(String[] args) {
ArrayToTree2 test = new ArrayToTree2();
for(int i = 0; i < inOrder.length; i++){
//将中序数组存放到哈希表中,并且数组的下标作为哈希表的值,对应的值作为哈希表的键
map.put(inOrder[i],i);
}
Node root = test.buildTree(inOrder, preOrder);
System.out.print("二叉树的后序遍历: ");
test.display(root);
}
/**
* 二叉树的后序遍历
* @param root
*/
public void display(Node root) {
if(root == null){
return;//如果当前节点是null,那么结束遍历
}
display(root.left);
display(root.right);
System.out.print(root.value+" ");
}
public Node buildTree(int[] inOrder, int[] preOrder) {
return buildTree(inOrder,0,inOrder.length,preOrder,0,preOrder.length);//方法的重载
}
private Node buildTree(int[] inOrder, int in_start, int in_end, int[] preOrder, int pre_start, int pre_end) {
if(pre_start >= pre_end){
//如果前序遍历得到的数组已经遍历完了,那么就将结束递归
return null;
}
int value = preOrder[pre_start];//前序遍历得到的数组第一个是根节点
Node root = new Node(value);//构造根节点
/*
//在中序遍历得到的数组中找到这个节点的下标
int i = in_start;//值得注意的是,这里不可以是从0开始遍历中序数组,因为最后将其分成左右子数组
for(; i < in_end; i++){
if(inOrder[i] == value){
break;//找到了根节点所在的位置
}
}
*/
int i = map.get(value);//通过哈希表,从而可以得到根节点对应的下标
//这一步十分重要的,统计左子树有多少个节点,不可以认为i就是想要的,因为考虑到了子数组的存在
int size = i - in_start;
//通过递归,从而对左右子数组进行构造,分别找到左右子数组中的根节点,然后再分别对他的左右子数组进行构造
root.left = buildTree(inOrder,in_start,i,preOrder,pre_start + 1,pre_start + 1 + size);
root.right = buildTree(inOrder,i + 1,in_end,preOrder,pre_start + 1 + size,pre_end);
return root;
}
}
中序遍历/后序遍历构造二叉树
根据一棵树的中序遍历与后序遍历构造二叉树。
注意:
你可以假设树中没有重复的元素。
题目来源:https://leetcode-cn.com/problems/construct-binary-tree-from-inorder-and-postorder-traversal/
思路和上面的思路是一样的,主要是找到中序数组中的左子数组和后序数组中对应的左子数组,中序数组中的右子数组和后序数组中对应的右子数组。其基本思路为:
1、我们都知道,后序遍历是首先遍历左子树,然后遍历右子树,最后遍历父节点,因此我们可以知道在后序数组中最后一个数就是根节点
2、通过循环遍历/或者利用哈希表,从而可以在中序数组中找到对应的根节点的下标,从而可以知道将中序数组分成两个子数组,并且根节点的左边是左子数组,右边是根节点的右子数组,并且可以知道左子树的节点个数size,并且通过这个size,从而可以在后序数组中找到对应的左右子树组
3、通过递归,从而对左右子数组进行上面的步骤
由于思路适合上面的思路是一样的,这里就不再画分析图了哈。
测试类:
import java.util.HashMap;
import java.util.Map;
public class ArrayToTree3 {
public static int[] inOrder = {4,5,8,10,12,15,18};//中序遍历得到的数组
public static int[] postOrder = {4,8,5,12,18,15,10 };//后序遍历得到的数组
//定义一个哈希表,从而存放中序数组的值及下表,并且值作为哈希表键,下标作为哈希表的值
public static Map<Integer,Integer> map = new HashMap<>();
public static void main(String[] args) {
ArrayToTree3 test = new ArrayToTree3();
for(int i = 0; i < inOrder.length; i++){
//将中序数组存放到哈希表中,并且数组的下标作为哈希表的值,对应的值作为哈希表的键
map.put(inOrder[i],i);
}
Node root = test.buildTree(inOrder, postOrder);
System.out.print("二叉树的后序遍历: ");
test.display(root);
}
/**
* 二叉树的前序遍历
* @param root
*/
public void display(Node root) {
if(root == null){
return;//如果当前节点是null,那么结束遍历
}
System.out.print(root.value+" ");
display(root.left);
display(root.right);
}
public Node buildTree(int[] inOrder, int[] postOrder) {
return buildTree(inOrder,0,inOrder.length,postOrder,0,postOrder.length);
}
private Node buildTree(int[] inOrder, int in_start, int in_end, int[] postOrder, int post_start, int post_end) {
if(post_start >= post_end){
//如果前序遍历得到的数组已经遍历完了,那么就将结束递归
return null;
}
int value = postOrder[post_end - 1];//后序遍历得到的数组最后一个是根节点
Node root = new Node(value);//构造根节点
/*
//在中序遍历得到的数组中找到这个节点的下标
int i = in_start;//值得注意的是,这里不可以是从0开始遍历中序数组,因为最后将其分成左右子数组
for(; i < in_end; i++){
if(inOrder[i] == value){
break;//找到了根节点所在的位置
}
}
*/
int i = map.get(value);//通过哈希表,从而可以得到根节点对应的下标
//这一步十分重要的,统计左子树有多少个节点,不可以认为i就是想要的,因为考虑到了子数组的存在
int size = i - in_start;
//通过递归,从而对左右子数组进行构造,分别找到左右子数组中的根节点,然后再分别对他的左右子数组进行构造
root.left = buildTree(inOrder,in_start,i,postOrder,post_start,post_start + size);
root.right = buildTree(inOrder,i + 1,in_end,postOrder,post_start + size,post_end - 1);//这里需要注意的是,右子数组在中序数组中对应后序数组中的范围是[post_start + size, post_end -1],post_end记得要减1,否则就会抛错java.lang.StackOverflowError
return root;
}
}
写的不好的地方,或者有错的,希望大家指正哈!