1.重建二叉树:根据前序和中序的遍历结果,重构建二叉树并返回其根节点
方法:递归
对于任意树,前序遍历的形式,根节点是前序遍历的第一个节点
【根节点,【左子树前序遍历结果】,【右子树的前序遍历结果】】
中序遍历的形式
【【左子树的中序遍历结果】,根节点,【右子树的中序遍历结果】】
只要我们在中序遍历中定位到根节点,那么我们就可以分别知道左子树和右子树中的节点数目。由于同一颗子树的前序遍历和中序遍历的长度显然是相同的,因此我们就可以对应到前序遍历的结果中,对上述形式中的所有左右括号进行定位。
这样以来,我们就知道了左子树的前序遍历和中序遍历结果,以及右子树的前序遍历和中序遍历结果,我们就可以递归地对构造出左子树和右子树,再将这两颗子树接到根节点的左右位置。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
private Map<Integer, Integer> map;
public TreeNode buildTree(int[] preorder, int[] inorder) {
int n = preorder.length;
map = new HashMap<>();
//将中序遍历结果数组加入map,key是数组值,value是数组的索引,方便查找前序遍历的子树的范围
for(int i =0;i<n;i++){
map.put(inorder[i],i);
}
TreeNode root =build(preorder,inorder,0,n-1,0,n-1);
return root;
}
//构建二叉树,preorder_left这是代表索引位置
public TreeNode build(int[] preorder, int[] inorder,int preorder_left,int preorder_right,int inorder_left,int inorder_right){
//递归必须明确啥时候,跳出递归,开始出栈
if(preorder_left>preorder_right) return null;
//先建立头节点
TreeNode node = new TreeNode(preorder[preorder_left]);
//确定左节点的范围,和右节点范围,方便建立节点的左右节点
//找到前序的头节点,对应中序的索引位置,确定左子树,右子树的范围
int inorder_root= map.get(preorder[preorder_left]);
//左子树和右子树的节点个数不同吧,两个不能共用len
int len = inorder_root-inorder_left;
// 先序遍历中「从 左边界+1 开始的 size_left_subtree」个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素
node.left = build(preorder,inorder,preorder_left+1,preorder_left+len,inorder_left,inorder_root-1);
node.right = build(preorder,inorder,preorder_left+len+1,preorder_right,inorder_root+1,inorder_right);
return node;
}
}
2.判断B是不是A的子树,这里是判断的B树的值是否跟A树相同
输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)
B是A的子结构, 即 A中有出现和B相同的结构和节点值。
算法流程:
recur(A, B) 函数:
终止条件:
1当节点 B 为空:说明树 B 已匹配完成(越过叶子节点),因此返回true ;
2当节点 A为空:说明已经越过树 A 叶子节点,即匹配失败,返回 false ;
3当节点 A 和 B的值不同:说明匹配失败,返回false ;
返回值:
1判断 A和 B的左子节点是否相等,即 recur(A.left, B.left) ;
2判断 A 和 B 的右子节点是否相等,即 recur(A.right, B.right) ;
3isSubStructure(A, B) 函数:
特例处理: 当 树 A为空 或 树 B 为空 时,直接返回 false ;但是在写程序时A!=NULL,否则无法判断以后的与的判断;
返回值: 若树 B 是树 A 的子结构,则必满足以下三种情况之一,因此用或 || 连接;
1以 节点 A为根节点的子树 包含树 B ,对应 recur(A, B);
2树 B 是 树 A 左子树 的子结构,对应 isSubStructure(A.left, B);
3树 B是 树 A右子树 的子结构,对应 isSubStructure(A.right, B);
以上 2. 3. 实质上是在对树 A做 先序遍历 。
调试错误点:((A != null) && (B != null) )
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isSubStructure(TreeNode A, TreeNode B) {
//1.前序遍历A树,这里AB != NULL;因为与操作,当为true时,才会判断后面的与操作
return ((A != null) && (B != null) )&&(recur(A,B) || isSubStructure(A.left,B) || isSubStructure(A.right,B));
}
//判断以A为根节点,和以B为根节点,的树相不相同。
boolean recur(TreeNode A,TreeNode B){
//1.终止条件
if(B == null) return true;//此时已经完成B树的匹配
if(A == null || A.val != B.val) return false;//A树已经到了最后的叶子节点的最后,以A为根节点B为根节点的树,不相同
return (recur(A.left,B.left) && recur(A.right,B.right));
}
}
3.镜像二叉树
思路:
显然,我们从根节点开始,递归地对树进行遍历,并从叶子节点先开始翻转得到镜像。如果当前遍历到的节点 \textit{root}root 的左右两棵子树都已经翻转得到镜像,那么我们只需要交换两棵子树的位置,即可得到以 \textit{root}root 为根节点的整棵子树的镜像。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode mirrorTree(TreeNode root) {
if(root == null) return null;
TreeNode left = mirrorTree(root.left);
TreeNode right = mirrorTree(root.right);
root.left = right;
root.right = left;
return root;
}
}
4.二叉树中和为某一值的路径
解题思路:本问题是二叉树方案搜索问题,使用回溯,包含先序遍历+路径记录
先序遍历过程中把节点加入数组中,当递归到链表的最后,再把链表中的值删除,最后回溯到,再遍历整棵左子树。最后回溯结束时,路径中的节点应该是为空的;
先序遍历: 按照 “根、左、右” 的顺序,遍历树的所有节点。
路径记录: 在先序遍历中,记录从根节点到当前节点的路径。当路径为 ① 根节点到叶节点形成的路径 且 ② 各节点值的和等于目标值 sum 时,将此路径加入结果列表。
/**
* 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 {
LinkedList<List<Integer>> res = new LinkedList<>();
//定义用于储存路径的单链表
//这里必须要写好了linkedlist类,应该时list没有这个方法
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> pathSum(TreeNode root, int target) {
recur(root,target);
return res;
}
void recur(TreeNode node,int target){
//判回溯条件
if(node == null) return;
path.add(node.val);
target -=node.val;
//递归开始回溯的条件,当到达叶子节点时,target同时为0
if(target == 0 && node.left ==null && node.right == null){
//这里必须新建一个链表,否则path是再不断更新,最后的path都是空了
res.add(new LinkedList(path));
}
//左子树递归
recur(node.left,target);
recur(node.right,target);
//递归回溯时要一步步删除path中的值
path.removeLast();
}
}
5.二叉树的深度
输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。
题目链接:力扣
分析:利用后序遍历,判断左子树和右子树的深度
方法一:后序遍历(DFS)
树的后序遍历 / 深度优先搜索往往利用 递归 或 栈 实现,本文使用递归实现。
关键点: 此树的深度和其左(右)子树的深度之间的关系。显然,此树的深度 等于 左子树的深度 与 右子树的深度 中的 最大值 +1
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int maxDepth(TreeNode root) {
//递归结束条件
if(root == null) return 0;
//每调用一次递归加一个值
return Math.max(maxDepth(root.left),maxDepth(root.right))+1;
}
}