513.找树左下角的值
思考
首先要搞清楚的一点就是:找的是树左下角的值,即深度最大的最左侧的节点。到此,我们可以想到之前写过的二叉树的层序遍历,只需在第一个while循环开始时记录queue中的第一个数值即可,直到最后一层,得到的就是左下角的值。除此之外,我们还可以选择使用递归法解决,依然是递归三部曲
1.明确递归函数的参数和返回类型【由于要检索到最深处所有要传入一个参数代表当前深度】
public void traversal(TreeNode root,int depth)
2.明确递归终止条件
//终止条件
if (root.left == null && root.right == null) {
if (depth > maxDepth) {
maxDepth = depth;
result = root.val;
}
}
3.明确单层递归逻辑【本题并未涉及到中节点的逻辑,只需要保证左节点优先即可】
代码展示
层序遍历:
class Solution {
int maxDepth = Integer.MIN_VALUE;
int result = 0;
public int findBottomLeftValue(TreeNode root) {
LinkedList<TreeNode> queue = new LinkedList<>();
queue.add(root);
while (!queue.isEmpty()) {
int size = queue.size();
TreeNode peek = queue.peek();
result = peek.val;
while (size-- > 0) {
TreeNode poll = queue.poll();
if (poll.left != null) {
queue.offer(poll.left);
}
if (poll.right != null) {
queue.offer(poll.right);
}
}
}
// traversal(root,0);
return result;
}
}
class TreeNode {
int val;
TreeNode left;
TreeNode right;
public TreeNode() {
}
public TreeNode(int val) {
this.val = val;
}
public TreeNode(int val, TreeNode left, TreeNode right) {
this.val = val;
this.left = left;
this.right = right;
}
}
递归法:
class Solution {
int maxDepth = Integer.MIN_VALUE;
int result = 0;
public int findBottomLeftValue(TreeNode root) {
traversal(root,0);
return result;
}
public void traversal(TreeNode root,int depth){
//终止条件
if (root.left == null && root.right == null) {
if (depth > maxDepth) {
maxDepth = depth;
result = root.val;
}
}
//前
if (root.left != null) {
depth++;
traversal(root.left, depth);
depth--;
}
//后
if (root.right != null) {
depth++;
traversal(root.right,depth);
depth--;
}
}
}
class TreeNode {
int val;
TreeNode left;
TreeNode right;
public TreeNode() {
}
public TreeNode(int val) {
this.val = val;
}
public TreeNode(int val, TreeNode left, TreeNode right) {
this.val = val;
this.left = left;
this.right = right;
}
}
总结
相对于递归法来说,本题更适合使用层序遍历的方法。如果使用递归法将涉及到回溯。
112. 路径总和i leetcode链接
思考
递归三部曲:
1.明确递归方法的参数和返回类型
public boolean hasPathSum(TreeNode root, int targetSum)
2.明确递归终止条件[当targetSum==0且遍历至于叶子节点时返回true]
// 如果当前节点为 null,说明路径结束,返回 false
if (root == null) {
return false;
}
//...
// 如果当前节点为叶子节点,且目标和为0,说明找到一条路径
if (root.left == null && root.right == null) {
return targetSum == 0;
}
3.明确单层递归逻辑
// 减去当前节点的值
targetSum -= root.val;
// 如果当前节点为叶子节点,且目标和为0,说明找到一条路径
if (root.left == null && root.right == null) {
return targetSum == 0;
}
// 递归检查左子树和右子树
if (hasPathSum(root.left, targetSum)) {
return true;
}
if (hasPathSum(root.right, targetSum)) {
return true;
}
代码展示
class Solution {
public boolean hasPathSum(TreeNode root, int targetSum) {
// 如果当前节点为 null,说明路径结束,返回 false
if (root == null) {
return false;
}
// 减去当前节点的值
targetSum -= root.val;
// 如果当前节点为叶子节点,且目标和为0,说明找到一条路径
if (root.left == null && root.right == null) {
return targetSum == 0;
}
// 递归检查左子树和右子树
if (hasPathSum(root.left, targetSum)) {
return true;
}
if (hasPathSum(root.right, targetSum)) {
return true;
}
return false;
}
}
class TreeNode {
int val;
TreeNode left;
TreeNode right;
public TreeNode() {
}
public TreeNode(int val) {
this.val = val;
}
public TreeNode(int val, TreeNode left, TreeNode right) {
this.val = val;
this.left = left;
this.right = right;
}
}
总结
要明确递归返回的条件,当targetSum==0且遍历至于叶子节点时返回true,并且会往回递归使targetSum变为上一个节点中targetSum的值,然后再继续遍历其他路径。【本题建议在递归函数内判断完root是否为空后就将targetSum-=root.val,后面再进行叶子节点的判断,这样可以避免漏掉根节点】
113.路径总和ii leetcode链接
思考
本题相当于是求二叉树所有路径 + 路径总和i 的结合体
递归三部曲
1.明确递归函数的参数和返回类型
public void preorderdfs(TreeNode root, int targetsum, List<List<Integer>> res, List<Integer> path)
2.明确递归函数的终止条件
if (root == null) {
return;
}
3.明确递归函数的单层逻辑设计
//在判断叶子节点之前先修改targetsum值,防止忽略单节点的情况
targetsum -= root.val;
//中
path.add(root.val);
//叶子节点
if (root.left == null && root.right == null && targetsum == 0) {
res.add(new ArrayList<>(path));
return;
}
//左
if (root.left != null) {
preorderdfs(root.left,targetsum,res,path);
path.remove(path.size() - 1);
}
//右
if (root.right != null) {
preorderdfs(root.right,targetsum,res,path);
path.remove(path.size() - 1);
}
代码展示
需要注意的是,每次递归回溯时需要将最后一个节点给remove掉
class Solution {
public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
List<List<Integer>> result = new ArrayList<List<Integer>>();
ArrayList<Integer> path = new ArrayList<>();
preorderdfs(root,targetSum,result,path);
return result;
}
public void preorderdfs(TreeNode root, int targetsum, List<List<Integer>> res, List<Integer> path) {
if (root == null) {
return;
}
targetsum -= root.val;
//中
path.add(root.val);
//叶子节点
if (root.left == null && root.right == null && targetsum == 0) {
res.add(new ArrayList<>(path));
return;
}
//左
if (root.left != null) {
preorderdfs(root.left,targetsum,res,path);
path.remove(path.size() - 1);
}
//右
if (root.right != null) {
preorderdfs(root.right,targetsum,res,path);
path.remove(path.size() - 1);
}
}
}
class TreeNode {
int val;
TreeNode left;
TreeNode right;
public TreeNode() {
}
public TreeNode(int val) {
this.val = val;
}
public TreeNode(int val, TreeNode left, TreeNode right) {
this.val = val;
this.left = left;
this.right = right;
}
}
总结
本题考察了 二叉树所有路径 + 路径总和i ,需要在完成遍历二叉树所有路径的代码逻辑的基础上判断是否满足路径总和的情况。
106.从中序与后序遍历序列构造二叉树 leetcode链接
思考
后序遍历的最后一个一定是根节点,得到根节点的数值后去中序遍历中找到根节点所在的下标,并根据这个下标将中序遍历的数组分为左右两部分,需要注意的是:在分割左右部分的时候要坚持循环不变量原则【左闭右开】。
递归三部曲:
1.明确递归函数的参数和返回类型
public TreeNode findNode(int[] inorder, int inBegin, int inEnd, int[] postorder, int postBegin, int postEnd)
2.明确递归函数的终止条件
if (inBegin >= inEnd || postBegin >= postEnd) { //保证左闭右开
return null;
}
3.明确递归函数的单层递归逻辑
if (inBegin >= inEnd || postBegin >= postEnd) { //保证左闭右开
return null;
}
Integer rootIndex = map.get(postorder[postEnd - 1]); //找出前序数组切割点
TreeNode root = new TreeNode(inorder[rootIndex]); //创建节点
int lenOfLeft = rootIndex - inBegin; //左区间长度
root.left = findNode(inorder,inBegin,rootIndex,postorder,postBegin,postBegin + lenOfLeft);
root.right = findNode(inorder,rootIndex + 1,inEnd,postorder,postBegin + lenOfLeft,postEnd - 1);
return root;
代码展示
class Solution {
HashMap<Integer,Integer> map;
public TreeNode buildTree(int[] inorder, int[] postorder) {
map = new HashMap<Integer,Integer>();
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 inBegin, int inEnd, int[] postorder, int postBegin, int postEnd) {
if (inBegin >= inEnd || postBegin >= postEnd) { //保证左闭右开
return null;
}
Integer rootIndex = map.get(postorder[postEnd - 1]); //找出前序数组切割点
//中
TreeNode root = new TreeNode(inorder[rootIndex]); //创建节点
int lenOfLeft = rootIndex - inBegin; //左区间长度
//左
root.left = findNode(inorder,inBegin,rootIndex,postorder,postBegin,postBegin + lenOfLeft);
//右
root.right = findNode(inorder,rootIndex + 1,inEnd,postorder,postBegin + lenOfLeft,postEnd - 1);
return root;
}
}
class TreeNode {
int val;
TreeNode left;
TreeNode right;
public TreeNode() {
}
public TreeNode(int val) {
this.val = val;
}
public TreeNode(int val, TreeNode left, TreeNode right) {
this.val = val;
this.left = left;
this.right = right;
}
}
总结
之前都是遍历二叉树,这一次是根据中序和后序遍历的数组构造一个二叉树,需要注意的是要始终坚持循环不变量原则,不然在处理边界值是会很混乱 。
105.从前序与中序遍历序列构造二叉树
思考
递归三部曲:
1.明确方法参数和返回类型
public TreeNode findNode(int[] preorder,int preBegin,int preEnd, int[] inorder,int inBegin,int inEnd)
2.明确递归终止条件
if (preBegin >= preEnd || inBegin >= inEnd){ //保证左闭右开
return null;
}
3.明确单层递归逻辑
Integer rootIndex = map.get(preorder[preBegin]);
TreeNode root = new TreeNode(inorder[rootIndex]);
int lenOfLeft = rootIndex - inBegin;
root.left = findNode(preorder,preBegin + 1,preBegin + 1+ lenOfLeft,inorder,inBegin,rootIndex);
root.right = findNode(preorder,preBegin + lenOfLeft + 1,preEnd,inorder,rootIndex + 1,inEnd);
return root;
代码展示
class Solution {
HashMap<Integer,Integer> map;
public TreeNode buildTree(int[] preorder, int[] inorder) {
map = new HashMap<Integer, Integer>();
for (int i = 0; i < inorder.length; i++) {
map.put(inorder[i],i);
}
return findNode(preorder,0, preorder.length,inorder,0,inorder.length);
}
//前序+中序
public TreeNode buildTree(int[] preorder, int[] inorder) {
map = new HashMap<Integer, Integer>();
for (int i = 0; i < inorder.length; i++) {
map.put(inorder[i],i);
}
return findNode(preorder,0, preorder.length,inorder,0,inorder.length);
}
public TreeNode findNode(int[] preorder,int preBegin,int preEnd, int[] inorder,int inBegin,int inEnd){
if (preBegin >= preEnd || inBegin >= inEnd){ //保证左闭右开
return null;
}
Integer rootIndex = map.get(preorder[preBegin]);
TreeNode root = new TreeNode(inorder[rootIndex]);
int lenOfLeft = rootIndex - inBegin;
root.left = findNode(preorder,preBegin + 1,preBegin + 1+ lenOfLeft,inorder,inBegin,rootIndex);
root.right = findNode(preorder,preBegin + lenOfLeft + 1,preEnd,inorder,rootIndex + 1,inEnd);
return root;
}
}
class TreeNode {
int val;
TreeNode left;
TreeNode right;
public TreeNode() {
}
public TreeNode(int val) {
this.val = val;
}
public TreeNode(int val, TreeNode left, TreeNode right) {
this.val = val;
this.left = left;
this.right = right;
}
}
总结
见上题