力扣刷题Day16

513.找树左下角的值

题目:力扣 

这道题用层序遍历的模板就可以直接找到左下角的节点了。

public int findBottomLeftValue(TreeNode root) {
        //层序遍历
        List<List<Integer>> result = new ArrayList<>();
 
        LinkedList<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        while(!queue.isEmpty()){
            int size = queue.size();//计算单层node的个数
            List<Integer> list = new ArrayList<>();//使用list储存单层node
            for(int i=0; i<size; i++){
                TreeNode node = queue.poll();
                list.add(node.val);
                
                //储存下一层node
                if(node.left != null) queue.add(node.left);
                if(node.right != null) queue.add(node.right);
 
            }
            result.add(list);//将单层node的list放入result
        }
 
        return result.get(result.size()-1).get(0);//取出最后一层的第一个节点
    }

但是因为只需要最后一层的第一个节点,因此可以优化 ---> 不用把所有的元素都储存,只储存每一层第一个元素

 public int findBottomLeftValue(TreeNode root) {
        //层序遍历
        LinkedList<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        int result = 0;//记录每一层的第一个节点值
        while(!queue.isEmpty()){
            int size = queue.size();//计算单层node的个数
            List<Integer> list = new ArrayList<>();//使用list储存单层node
            for(int i=0; i<size; i++){
                TreeNode node = queue.poll();
                list.add(node.val);
                
                //储存下一层node
                if(node.left != null) queue.add(node.left);
                if(node.right != null) queue.add(node.right);
 
            }
            result = list.get(0);//更新result
        }
 
        return result;
    }

这道题如果采用递归的算法来做的话,思路上会比较复杂:

                首先需要了解,寻找树左下角的值,并不等于一定是左叶子,假如说一个二叉树的最大深度位置只有右节点,那么这个值也是树左下角的值。

因此在递归的时候可以理解成:寻找最大深度位置的第一个节点。

这里采用的是前序遍历的方式 - 中间节点处并没有任何处理。

递归思路:

  1. 确定输入和输出:输入(root);输出(无) --- 这里在寻找最大深度的时候需要用到回溯的方法,因此需要两个全局变量<1. 最大深度;2.最大深度的第一个节点>
  2. 确定终止条件: 当遇到叶子节点的时候,判断此时的深度是否大于最大深度,如果大于则需要更新最大深度和第一个节点值
  3. 单层遍历逻辑:先遍历左节点、再遍历右节点。保证最大深度的第一个节点值在左侧。

代码实现:

<这里需要注意回溯的写法 - 以及回溯的隐藏写法>

隐藏回溯的写法:

class Solution {
    public int findBottomLeftValue(TreeNode root) {
        bfs(root, 0);
        return result;
    }

    int result = 0;
    int maxDepth = -1;
    public void bfs(TreeNode root, int depth){
        //终止条件
        if(root.left == null && root.right == null){
            if(depth > maxDepth){
                maxDepth = depth;
                result = root.val;
            } 
            return;
        }

        //左
        if(root.left != null){
            bfs(root.left, depth+1);//隐藏回溯
        }

        //右
        if(root.right != null){
            bfs(root.right, depth+1);//隐藏回溯
        }

        return;
    }
}

显示回溯的写法:<需要回溯的变量不能在函数中有变化>

class Solution {
    public int findBottomLeftValue(TreeNode root) {
        bfs(root, 0);
        return result;
    }

    int result = 0;
    int maxDepth = -1;
    public void bfs(TreeNode root, int depth){
        //终止条件
        if(root.left == null && root.right == null){
            if(depth > maxDepth){
                maxDepth = depth;
                result = root.val;
            } 
            return;
        }

        //左
        if(root.left != null){
            depth++;
            bfs(root.left, depth);
            depth--;//回溯
        }

        //右
        if(root.right != null){
            depth++;
            bfs(root.right, depth);
            depth--;//回溯
        }

        return;
    }
}

参考资料:代码随想录 

112. 路径总和 

题目:力扣 

这道题应该是递归+回溯的做法。

可以采用前序遍历的方式 - 边遍历边累加值

递归思路:

  1. 输入和输出:输入( root,当前累加值);输出(无)---采用全局变量 - target,是否存在标志1/0
  2. 终止条件:当遍历到叶子节点的时候,加入叶子节点的值,判断当前累加值是否为target
  3. 单层循环逻辑:从左边子树遍历,再遍历右边子树。 -- 需要回溯

在实际实现的时候需要注意单层逻辑中 - 添加的节点值是什么(这里统一是添加要遍历的节点的值)【不能主方法添加的要遍历的节点的值,辅助方法的单层逻辑添加的仍然是上一层节点的值,要注意统一】

class Solution {

    public boolean hasPathSum(TreeNode root, int targetSum) {
        if(root == null){
            return false;
        }

        target = targetSum;
        bfs(root, root.val);

        return isValid == 1;

    }

    int target = 0;
    int isValid = 0;
    public void bfs(TreeNode root, int sum){
        //终止条件
        if(root.left == null && root.right == null){
            if(target == sum){
                isValid = 1;
            }
            return;
        }

        //左
        if(root.left != null){
            sum += root.left.val;
            bfs(root.left, sum);
            sum -= root.left.val;//回溯
        }
        
        if(root.right != null){
            sum += root.right.val;
            bfs(root.right, sum);
            sum -= root.right.val;//回溯
        }

        return;

    }


}

另一种实现方法是:1)把目标值减去节点的值,那么一直到叶子节点,如果目标值变为0,说明是存在这条路径的。这样在代码实现上会更加简洁。2)不用标志进行判断是否存在,直接把回溯函数改为boolean,存在的话就直接返回true。

这样实现的时候就不需要再设置两个全局变量来记录了target 和 存在标志。

需要注意的是:如果通过boolean的话进行递归,需要传递单层循环return的值。--->  if(bfs(root.left, sum)) return true; 如果不传递的话,可能这个地方的boolean值仍然是false。

代码实现:

class Solution {

    public boolean hasPathSum(TreeNode root, int targetSum) {
        if(root == null){
            return false;
        }

        return bfs(root, targetSum - root.val);

    }

    public boolean bfs(TreeNode root, int sum){
        //终止条件
        if(root.left == null && root.right == null){
            if(sum == 0){
                return true;
            }
            return false;
        }

        //左
        if(root.left != null){
            sum -= root.left.val;
            if(bfs(root.left, sum)) return true;
            sum += root.left.val;//回溯
        }
        
        if(root.right != null){
            sum -= root.right.val;
            if(bfs(root.right, sum)) return true;
            sum += root.right.val;//回溯
        }

        return false;

    }


}

参考资料:代码随想录 

113. 路径总和ii 

题目:力扣 

这道题和112的解题思路是一样的,直接用递归+回溯的方法做,并且记录符合的路径就可以。

区别:在终止条件中,若是符合,需要将list放到结果集中。

需要注意的是:放入的时候要放new ArrayList<>(list)(重新复制一份), 而不是list,引用指针是不行的。

class Solution {
    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        target = targetSum;
        
        result = new ArrayList<>();
        if(root == null){
            return result;
        }

        List<Integer> list = new ArrayList<>();
        list.add(root.val);
        bfs(root, root.val, list);

        return result;
    }

    List<List<Integer>> result;
    int target = 0;
    public void bfs(TreeNode root, int sum, List<Integer> list){
        //终止条件
        if(root.left == null && root.right == null){
            if(target == sum){
                result.add(new ArrayList<>(list));
            }
            return;
        }

        //左
        if(root.left != null){
            sum += root.left.val;
            list.add(root.left.val);
            bfs(root.left, sum, list);
            sum -= root.left.val;//回溯
            list.remove(list.size()-1);
        }
        
        if(root.right != null){
            sum += root.right.val;
            list.add(root.right.val);
            bfs(root.right, sum, list);
            sum -= root.right.val;//回溯
            list.remove(list.size()-1);
        }

        return;

    }
}

106.从中序与后序遍历序列构造二叉树 

 题目:力扣

106.从中序与后序遍历序列构造二叉树 

已知中序和后序,二叉树是很容易被画出来的。

在每一次的分割都都是单层的循环。

递归逻辑:

  1. 输入和输出:输入(后序list,中序list中的左侧index,中序list中的右侧index,后序list中的左index,后序list中的右index);输出 -  根节点;
  2. 终止条件:边界左侧大于等于边界右侧,说明是空节点,返回null;<注意左闭右开原则>
  3. 单层循环逻辑:通过后序list找到最后一位,确定此处的节点值index,生成一个新节点,将节点值放入新节点中;将中序list进行分割,递归二分 - index前的部分为左子树左右边界、index后的部分为右子树(左右边界);对后序list也进行分割,找到左子树位置的index(左右边界),找到右子树位置的index(左右边界)---> 根据中序和后序的左右边界找root的左节点;根据中序和后序的左右边界找root的右节点

这里需要注意的是1)边界的计算方式(左闭右开) - 2)这里采用的遍历方式是前序遍历(先处理中间节点,然后再往左侧和右侧遍历);

3)为了能够快速找到中序list中节点的index,在主方法中先用全局变量map来储存了中序遍历中元素的index。

 代码实现:

class Solution {

    Map<Integer, Integer> map = new HashMap<>();
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        if(inorder == null || postorder == null){
            return null;
        }

        //采用map储存中序节点的index,方便快速找到位置
        for(int i=0; i< inorder.length; i++){
            map.put(inorder[i], i);
        }

        return  buildTreeHelper(postorder, 0, inorder.length, 0, postorder.length);

       

    }
    
    public TreeNode buildTreeHelper(int[] postorder, int inorder_left, int inorder_right, int postorder_left, int postorder_right) {
        //终止条件
        if(inorder_left >= inorder_right || postorder_left >= postorder_right){
            return null;
        }

        int value = postorder[postorder_right-1];
        TreeNode root = new TreeNode(value);
        int index = map.get(value);//中序遍历中节点的index位置
        //左侧
        //inorder_left
        //index

        //postorder_left
        //postorder_left+index-inorder_left
        
        //右侧
        //index+1
        //inorder_right

        //postorder_left+index-inorder_left
        //postorder_right-1


        root.left = buildTreeHelper(postorder, inorder_left, index, postorder_left, postorder_left+index-inorder_left);
        root.right = buildTreeHelper(postorder, index+1, inorder_right, postorder_left+index-inorder_left, postorder_right-1);

        return root;
    }

    
}

 参考资料:代码随想录

105.从前序与中序遍历序列构造二叉树 

题目:力扣

 这道题的解题思路是和106一样的,边界值的求法稍有不同。

同样是采用了前序遍历的方式,左闭右开求左右边界。

递归逻辑:

  1. 输入和输出:输入(前序list,后序list,中序list中的左侧index,中序list中的右侧index,后序list中的左index,后序list中的右index);输出 -  根节点;
  2. 终止条件:边界左侧大于等于边界右侧,说明是空节点,返回null;<注意左闭右开原则>
  3. 单层循环逻辑:通过前list找到第一位,确定此处的节点值index,生成一个新节点,将节点值放入新节点中;将中序list进行分割,递归二分 - index前的部分为左子树左右边界、index后的部分为右子树(左右边界);对前list也进行分割,找到左子树位置的index(左右边界),找到右子树位置的index(左右边界)---> 根据中序和前序的左右边界找root的左节点;根据中序和前序的左右边界找root的右节点

同样为了能够快速找到中序list中的index,采用map将中序遍历的index进行储存。

代码实现:

class Solution {
    Map<Integer, Integer> map = new HashMap<>();
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        if(preorder == null || inorder == null){
            return null;
        }

        for(int i=0; i<inorder.length; i++){
            map.put(inorder[i], i);
        }


        return buildTreeHelper(preorder, 0, preorder.length, 0, inorder.length);

    }

     public TreeNode buildTreeHelper(int[] preorder, int preorder_left, int preorder_right, int inorder_left, int inorder_right) {
        //终止条件
        if(preorder_left >= preorder_right || inorder_left >= inorder_right){
            return null;
        }

        TreeNode root = new TreeNode(preorder[preorder_left]);
        int index = map.get(preorder[preorder_left]);

        //左边
        //preorder_left+1
        //preorder_left+1+index-inorder_left

        //inorder_left
        //index

        //右边
        //preorder+1+index-inorder_left
        //preorder_right
        
        //index+1
        //inorder_right

        root.left = buildTreeHelper(preorder, preorder_left+1, preorder_left+1+index-inorder_left, inorder_left, index);
        root.right = buildTreeHelper(preorder, preorder_left+1+index-inorder_left, preorder_right, index+1, inorder_right);
        
        return root;
    }

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值