513.找树左下角的值
题目链接/文章讲解/视频讲解:https://programmercarl.com/0513.%E6%89%BE%E6%A0%91%E5%B7%A6%E4%B8%8B%E8%A7%92%E7%9A%84%E5%80%BC.html
递归法思路:首先使用递归就得先找到最深的一层,再开始找最左边的一层。那么就是只要我们遍历到叶子节点,就表示到了最后一个。同时我们要优先遍历左边的子树,因为左右子树深度相同的情况下,我们只记录最左边的值,又因为使用递归遍历所有的节点所以还需要回溯不断的向上返回到父节点
// 递归法
class Solution {
// 全局变量
public int Deep=-1; // 最大深度
public int value=0;
public int findBottomLeftValue(TreeNode root) {
value = root.val;
findLeftValue(root,0);
return value;
}
// 返回值:因为只需要遍历二叉树,所以不需要返回值
// deep:记录当前节点的深度
public void findLeftValue(TreeNode node,int deep){
// 终止条件:判断是否为叶子节点
// 题目说明了,最少有一个节点,所以不用担心Node==null的情况
if(node.left==null && node.right==null){
// 这里为什么是>,而不是>=
// 因为我们优先遍历左子树,如果左右子树深度相同的情况下,加了=那么这个if就会被继续执行一次,这样就会导致记录的不是最左边的值
// 因为是先遍历左边,所以在深度相同的情况下,保证只记录一次
if(deep>Deep){
value = node.val;
Deep = deep;
}
}
// 向左遍历
if(node.left!=null) {
deep++;
findLeftValue(node.left,deep);
deep--; // 回溯:回到父节点,方便继续向右查找,同时深度也要不断-1;
}
if(node.right!=null) {
deep++;
findLeftValue(node.right,deep);
deep--;
}
}
}
// 迭代法:使用层序遍历
// 思路:不断记录每层最左边的值
public int findBottomLeftValue(TreeNode root) {
Queue<TreeNode> q = new LinkedList<>();
int res=0;
q.add(root);
while(!q.isEmpty()){
int size = q.size();
for(int i=0; i<size; i++){
TreeNode cur = q.poll();
// 只要第一次进行for就表示是最左边的值,然后进行记录
if(i==0) res = cur.val;
if(cur.left!=null) q.add(cur.left);
if(cur.right!=null) q.add(cur.right);
}
}
return res;
}
112.路径总和
题目链接/文章讲解/视频讲解:https://programmercarl.com/0112.%E8%B7%AF%E5%BE%84%E6%80%BB%E5%92%8C.html
思路:思路是比较简单的,就是不断记录路径,只要有一条路径满足要求,那么就不再遍历直接返回
class Solution {
public boolean hasPathSum(TreeNode root, int targetSum) {
if(root == null) return false;
targetSum -= root.val;
return traversal(root,targetSum);
}
// 返回值:因为我们只要找到了一条路径,后后面的路径就没必要继续遍历,所以返回值就是boolean
// 参数:count:不断减少,直至等于0,因为累计减只要最后等于0就满足要求,如果累加,就还要在传递一个目标值来判断
public boolean traversal(TreeNode root, int count){
// 终止条件:只要当前节点是叶子节点就是一条完整的路径,然后如果count==0了,就表示这条路径符合要求直接返回
if(root.left==null && root.right==null) return count == 0;
// 向左遍历
if(root.left!=null){
// 如果找到了一条路径符合要求,那么直接返回就不需要继续遍历了
// count-root.left.val:这里就隐藏了回溯的过程
// java中基本类型都是值传递,a-b是不会影响到a的值的
// 所以就需要像上一题那样把count加回来,进行回溯
if(traversal(root.left,count-root.left.val)) return true;
}
if(root.right!=null){
if(traversal(root.right,count-root.right.val)) return true;
}
// 不存在符合要求的路径
return false;
}
}
106.从中序与后序遍历中构造二叉树
思路:首先我们可以想到使用后序就能确定根节点的位置,然后查找根节点再中序数组中的位置,来切割中序数组分成左右子树的两个区间,然后根据左右子树的区间,再后序数组中继续切割
- 如果数组大小为0,那就是空节点
- 如果不为空,那么后序数组中最后一个元素就是根节点
- 找到根节点在中序数组中的位置作为切割点
- 中序数组切割成左右两个区间(中序左子树和中序右子树)
- 然后根据切割出来的中序左子树的数量,去切割后序(后序左子树和后序右子树)
- 最后递归处理左右子树区间
// 解法一
class Solution {
// 方便查找中序数组的元素索引
Map<Integer,Integer> map = new HashMap<>();
public TreeNode buildTree(int[] inorder, int[] postorder) {
// 将中序数组存入map,方便查找
for(int i=0; i<inorder.length; i++){
// [值,索引]
map.put(inorder[i],i);
}
return findNode(inorder,0,inorder.length,postorder,0,postorder.length);
}
// 返回值:因为需要不断创建节点,所以返回值就是TreeNode
// 参数:两个数组和起始,结尾索引,因为根节点会不断变化,所以要不断的查找左右子树在数组中的区间
public TreeNode findNode(int[] inorder ,int inBegin,int inEnd,
int[] postorder, int postBegin, int postEnd){
// 步骤1
if(inBegin>=inEnd || postBegin>=postEnd) return null;
// 利用后序数组获取到根节点在中序数组中的索引,用来切割中序数组
// 这样就可以根据根节点,来划分出左右子树的区间,左边就是左子树的区间,右边同理
// 因为后序是:左右中,所以数组的最后一个元素一定是根节点
// 步骤2
int rootIndex = map.get(postorder[postEnd-1]);
// 创建节点
TreeNode root = new TreeNode(inorder[rootIndex]);
// 根据根节点在中序数组中的位置,计算出中序数组中的左子树的数量
// 这样就可以用来切割后序数组中左子树的区间
// 步骤3
int lenOfLeft = rootIndex - inBegin;
// 遍历左中序和左后序(遵循左闭右开的原则)
// 左中序数组:[起始索引,根节点索引)
// 起始索引+lenOfLeft:根据中序数组中左子树的数量切割出来的后序左子树区间
// 因为根节点会不断变化,所以起始索引也会不断变化,需要加上
// 左后序数组:[起始索引,起始索引+lenOfLeft),
// 步骤4,5,6
root.left = findNode(inorder,inBegin,rootIndex
,postorder,postBegin,postBegin+lenOfLeft);
// 遍历右中序和右后序(同样遵循左闭右开)
// 右中序数组:[根节点索引+1,数组长度)
// 右后序数组:[起始索引+lenOfLeft,数组长度-1),-1是因为要去掉最后的根节点
root.right = findNode(inorder,rootIndex+1,inEnd
,postorder,postBegin+lenOfLeft,postEnd-1);
return root;
}
}
// 解法二
class Solution {
public TreeNode buildTree(int[] inorder, int[] postorder) {
// 步骤1:数组为0直接返回
if(postorder.length == 0 || inorder.length == 0)
return null;
return buildHelper(inorder, 0, inorder.length, postorder, 0, postorder.length);
}
private TreeNode buildHelper(int[] inorder, int inorderStart, int inorderEnd, int[] postorder, int postorderStart, int postorderEnd){
if(postorderStart == postorderEnd)
return null;
// 步骤2:查找根节点
int rootVal = postorder[postorderEnd - 1];
// 根据后序数组的最后一个元素创建根节点
TreeNode root = new TreeNode(rootVal);
// 根节点在中序数组的索引
int middleIndex;
// 步骤3:查找索引
for (middleIndex = inorderStart; middleIndex < inorderEnd; middleIndex++){
if(inorder[middleIndex] == rootVal)
break;
}
// 步骤4:切割中序数组(一定要先切割中序数组,因为我们无法直接在后序数组中切割出左右子树的区间)
// 根据根节点在中序数组的索引切割出左子树的中序区间(同时保持左闭右开的原则)
int leftInorderStart = inorderStart;
int leftInorderEnd = middleIndex;
// 右子树的中序区间
int rightInorderStart = middleIndex + 1;
int rightInorderEnd = inorderEnd;
// 步骤5
// 根据中序数组切割出来的左右区间大小就可以在后序数组中也切割出左右区间
int leftPostorderStart = postorderStart;
int leftPostorderEnd = postorderStart + (middleIndex - inorderStart);
int rightPostorderStart = leftPostorderEnd;
// -1:找右区间时要把根节点去掉
int rightPostorderEnd = postorderEnd - 1;
// 步骤6:递归
root.left = buildHelper(inorder, leftInorderStart, leftInorderEnd, postorder, leftPostorderStart, leftPostorderEnd);
root.right = buildHelper(inorder, rightInorderStart, rightInorderEnd, postorder, rightPostorderStart, rightPostorderEnd);
return root;
}
}