①、找树左下角的值
给定一个二叉树的 根节点
root
,请找出该二叉树的 最底层 最左边 节点的值。假设二叉树中至少有一个节点。
事例:
输入: root = [2,1,3] 输出: 1
思路:
①迭代
涉及到最深层,故可以使用层序遍历,将每层中的所有值都存储到一个集合中,最后获取最后一层的集合再获取第一个数即可。
代码:
public int findBottomLeftValue(TreeNode root) {
//迭代
List<List<Integer>> lists = new ArrayList<>();
Deque<TreeNode> deque = new LinkedList<>();
deque.offer(root);
while(!deque.isEmpty()){
int n = deque.size();
List<Integer> help = new ArrayList<>();
for(int i = 0;i < n;i++){
TreeNode tmp = deque.poll();
if(tmp.left != null) deque.offer(tmp.left);
if(tmp.right != null) deque.offer(tmp.right);
help.add(tmp.val);
}
lists.add(help);
}
int count = lists.size();
return lists.get(count - 1).get(0);
}
②、递归
由于要最后一层,故递归需要采取从上到下求,即中左右求高度。每次递归传层数,当遇到更深层时,改变结果值,由于左比右更早递归,故获得到的值为最后一层的最左边的值。
代码:
private int maxDepth = 0;
private int res = 0;
public int findBottomLeftValue(TreeNode root) {
//递归
getDepth(root,1);
return res;
}
public void getDepth(TreeNode root,int depth){
if(depth > maxDepth){ //中
res = root.val;
maxDepth = depth;
}
if(root.left == null && root.right == null) return ;
if(root.left != null){ //左
depth++;
getDepth(root.left,depth);
//回溯
depth--;
}
if(root.right != null){ //右
depth++;
getDepth(root.right,depth);
//回溯
depth--;
}
return;
}
②、路径总和
给你二叉树的根节点
root
和一个表示目标和的整数targetSum
。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和targetSum
。如果存在,返回true
;否则,返回false
。叶子节点 是指没有子节点的节点。
事例:
输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22 输出:true 解释:等于目标和的根节点到叶节点路径如上图所示。
思路:
跟求二叉树的路径一样,传入结点和当前的总和值,当遇到的是叶子结点时,判断目前总和是否为目标值,若为结果值,结束遍历,若不是结果值,则继续递归。
代码:
rivate List<Integer> help = new ArrayList<>();
public boolean hasPathSum(TreeNode root, int targetSum) {
// getLists(root,0);
// for(int x : help){
// if(x == targetSum) return true;
// }
int res = getLists(root,0,targetSum);
if(res == -1) return true;
return false;
}
// public void getLists(TreeNode root,int cur){
// if(root == null) return;
// int tmp = root.val;
// cur += tmp;
// if(root.left == null && root.right == null){
// //叶子结点
// help.add(cur);
// return;
// }
// getLists(root.left,cur);
// getLists(root.right,cur);
// }
public int getLists(TreeNode root,int cur,int targetSum){
if(root == null) return 0;
int res = 0;
int tmp = root.val;
cur += tmp;
if(root.left == null && root.right == null){
//叶子结点
help.add(cur);
if(cur == targetSum) res = -1;
return res;
}
int left = getLists(root.left,cur,targetSum);
if(left == -1) return -1;
int right = getLists(root.right,cur,targetSum);
if(right == -1) return -1;
return res;
}
递归的返回结果可以标志是否结束,若为-1,则直接return 结束递归,这样可以提高速率。
③、路径总和Ⅱ
给你二叉树的根节点
root
和一个整数目标和targetSum
,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。叶子节点 是指没有子节点的节点。
事例:
输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22 输出:[[5,4,11,2],[5,8,4,5]]
思路:
采用递归,跟普通的路径题一样,传入当前路径和结果集,当遇到叶子结点时,判断目前的总和是否为目标值,若是,则加入结果集。与以往不同的是,这次传入的是引用参数,每次加入结果集需要新建新的集合和参数也要进行回溯,避免后续操作影响结果。
代码:
public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
List<List<Integer>> res = new ArrayList<>();
if(root == null) return res;
getLists(root,targetSum,res,new ArrayList<>());
return res;
}
public void getLists(TreeNode root,int targetSum,
List<List<Integer>> res,List<Integer> path){
path.add(root.val);
if(root.left == null && root.right == null){
//遇到叶子结点
if(targetSum - root.val == 0){
//符合条件
res.add(new ArrayList<>(path));
}
return;
}
if(root.left != null){
//遍历左节点
getLists(root.left,targetSum - root.val,res,path);
//回溯
path.remove(path.size() - 1);
}
if(root.right != null){
//遍历右节点
getLists(root.right,targetSum - root.val,res,path);
//回溯
path.remove(path.size() - 1);
}
}
④、从中序与后序遍历序列构造二叉树
给定两个整数数组
inorder
和postorder
,其中inorder
是二叉树的中序遍历,postorder
是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。
事例:
输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3] 输出:[3,9,20,null,null,15,7]
思路:
中序遍历结构为左中右,后序遍历为左右中,故可以从后序遍历尾巴依次取得根结点,然后切割中序遍历,获得左子树和右子树,然后再切割后序遍历,获得左子树和右子树的后序遍历,依次递归得到最终树结构。
代码:
Map<Integer,Integer> map = new HashMap<>();
public TreeNode buildTree(int[] inorder, int[] postorder) {
if(postorder == null || postorder.length == 0) return null;
for(int i = 0;i < inorder.length;i++){
map.put(inorder[i],i);
}
return findNode(inorder,0,inorder.length,postorder,0,postorder.length);
}
public TreeNode findNode(int[] inorder,int inOpen,int inEnd,
int[] postorder,int postOpen,int postEnd){
//左闭右开原则
if(inOpen >= inEnd || postOpen >= postEnd){
//不满足左闭右开原则 即数组没有空间
return null;
}
int rootVal = postorder[postEnd - 1];
int index = map.get(rootVal);
TreeNode root = new TreeNode(rootVal);
int leftNum = index - inOpen; //记录左子树有多少结点 方便后序数组切割
root.left = findNode(inorder,inOpen,index,postorder,postOpen,postOpen + leftNum);
root.right = findNode(inorder,index + 1,inEnd,postorder,postOpen + leftNum,postEnd - 1);
return root;
}
该代码采用map快速获得中序遍历中当前结点的索引,方便后续切割,leftNum表示以该结点为根节点的子树的左子树结点个数,因此可以直接将后序遍历中的左右子树分割。
总体采用左闭右开思想。
⑤、从前序与中序遍历序列构造二叉树
给定两个整数数组
preorder
和inorder
,其中preorder
是二叉树的先序遍历,inorder
是同一棵树的中序遍历,请构造二叉树并返回其根节点。
事例:
输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7] 输出: [3,9,20,null,null,15,7]
总体做法与中序后序一样,原本根结点从后序遍历的后面取,这道题变成从前序的前面取。
代码:
Map<Integer,Integer> map = new HashMap<>();
public TreeNode buildTree(int[] preorder, int[] inorder) {
if(preorder == null || preorder.length == 0) return null;
for(int i = 0;i < inorder.length;i++){
map.put(inorder[i],i);
}
return buildNode(preorder,0,preorder.length,inorder,0,inorder.length);
}
public TreeNode buildNode(int[] preorder,int preOpen,int preEnd,
int[] inorder,int inOpen,int inEnd){
// 左闭右开
if(preOpen >= preEnd || inOpen >= inEnd){
//没有元素
return null;
}
int rootVal = preorder[preOpen];
preOpen++;
TreeNode root = new TreeNode(rootVal);
int index = map.get(rootVal); //获取索引 方便切割
int leftNum = index - inOpen; //左子树的结点个数 方便切割前序数组
//递归构造左右子树
root.left = buildNode(preorder,preOpen,preOpen + leftNum,inorder,inOpen,index);
root.right = buildNode(preorder,preOpen + leftNum,preEnd,inorder,index + 1,inEnd);
return root;
}