513.找左下角的值
迭代法较简单理解,递归法较难理解!
思路
迭代法思路:按照层序遍历为模板,然后对每层遍历到的第一个节点做逻辑处理就完成了。
递归法思路:要找最左下角的节点,这里要满足两个条件:1.最深 2.左下角;
只要保证遍历顺序是左优先就可以获得左的节点,所以使用中序或者后序遍历都可以;
然后是“最”该如何获得呢?这个其实是深度最深的意思,所以一直递归获得深度最深的;所以在中的处理逻辑是—第一层判断:当前节点的左右节点为空,此判断获得所有最左边的节点;第二层判断:判断maxdeep是否大于每次迭代获得的deep,若小于就重新给maxdeep赋值当前最深的deep,满足要求的value1就是当前节点的值。
注意
初始化maxdeep要注意等于多少,因为不排除深度为0的情况,所以它不能为0,因此赋值保证小于0就好。
实现代码
递归法:
//找最左下角,遍历顺序是左优先的
//要找满足深度最深的
class Solution {
int value=0;
int maxdeep=-1;
public int findBottomLeftValue(TreeNode root) {
getDeep(root,0);
return value;
}
public void getDeep(TreeNode root,int deep){
if(root==null){
return;
}
//左
getDeep(root.left,deep+1);
//中
if(root.left==null&&root.right==null){
if(deep>maxdeep){
maxdeep=deep;
value=root.val;
}
}
//右
getDeep(root.right,deep+1);
}
}
112.路径总和
是否需要返回值辨析
对于一个方法是否需要有返回值的问题,就以树来说:
1.若需要将整棵树都遍历一遍才能得出最后结果的,那么不需要返回值;
2.若是当一遇到满足条件就马上返回的情况,也就是不一定需要遍历完整棵树就可以得出结果的,就需要返回值。
思路
题目要求需要从根节点出发一直到叶子节点,整条路径的节点值相加等于给出的目标值,说明存在这样的路径的话就返回true。这里可以采用任一种遍历方式;对中的处理是:首先是当前节点的左右子树都为空,然后第二层判断条件是targetSum的值为0,这里就要说明一下:每遍历一个节点就用当前的targetSum-当前节点值,最后若为0说明符合条件马上返回true;之后就是当前节点的左右节点的递归了。
注意
要判断一条路径的总节点值是否与目标值相等,第一想法就是先相加然后再和目标值匹配,这个方法有点复杂;
所以可以用目标值减去每个遍历节点的值,看最后是否等于0就可以判断是否相等了,很多情况都可以用到,一定要记得这个思路!!!
实现代码
class Solution {
public boolean hasPathSum(TreeNode root, int targetSum) {
return getTarget(root,targetSum);
}
public boolean getTarget(TreeNode root,int targetSum){
if(root==null){
return false;
}
targetSum-=root.val;
if(root.left==null&&root.right==null){
if(targetSum==0){
return true;
}
}
boolean left=hasPathSum(root.left,targetSum);
if(left){
return true;
}
boolean right=hasPathSum(root.right,targetSum);
if(right){
return true;
}
return false;
}
}
相关题目
113.路径总和2
思路
此题目是在112.路径总和后的相关题目,这个需要将所有满足要求的路径找到,所以不设置返回值;然后要将满足要求的节点放入一个小集合,最后再将所以小集合放入到大集合中;这里要注意要回溯,如果这条路径满足要求,或者不满足要求,那都需要回溯去找其他路径。
注意
在将小集合加入大集合中时,每次都需要新建小集合list,这个可以这样理解:就是小集合是用来存放一条满足要求的路径,那么有不同的路径就需要集合存的内容不一样,所以每一条路径的开头都要new ArrayList<>(list)表示新建。
实现代码
public void getAll(TreeNode root,int targetSum,List<Integer> list,List<List<Integer>> res){
//要获得所有符合要求的路径,一定要记得在递归后进行回溯
if(root==null){
return;
}
list.add(root.val);
targetSum-=root.val;
if(root.left==null&&root.right==null){
if(targetSum==0){
//因为list是引用传递,为了防止递归的时候分支污染,我们要在每个路径中都要新建一个list
res.add(new ArrayList<>(list));
}
return;
}
if(root.left!=null){
getAll(root.left,targetSum,list,res);//递归
list.remove(list.size()-1);//回溯
}
if(root.right!=null){
getAll(root.right,targetSum,list,res);
list.remove(list.size()-1);
}
}
}
106.从中序与后序遍历序列构造二叉树
这道题我理解也可以按照步骤写出代码,但是一直出现错误,不知道为什么!!!
思路
后序遍历是:左右中,说明在后序数组中最后一个数是根节点数;
用map存储前序数组里面的数,然后定位到根节点数的位置,在这个位置的左边是左子树,右边是右子树,以此根节点为分割点;然后统计左子树的长度用来切割后序数组;
因为后序数组的最后一个数是根节点所以一定要记得将这个数减去,在切割后序遍历的右子树范围要将这个排除。
实现代码
class Solution {
Map<Integer, Integer> map; // 方便根据数值查找位置
public TreeNode buildTree(int[] inorder, int[] postorder) {
map = new HashMap<>();
for (int i = 0; i < inorder.length; i++) { // 用map保存中序序列的数值对应位置
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;
}
int 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;
}
}
相关题目
105.从前序与中序遍历序列构造二叉树
总结
这三道题都要加强理解,特别是按照遍历得出序列来构造二叉树,里面的一些逻辑还有点不清晰。
二刷的重点关注对象,并且要把里面的知识点要提取出来。