代码随想录算法训练营day18 | 513.找树左下角的值 112. 路径总和 113.路径总和ii 106.从中序与后序遍历序列构造二叉树 105.从前序与

98 阅读7分钟

513.找树左下角的值

给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。
假设二叉树中至少有一个节点。

思路

使用层次遍历时,由于我们在每一层都是取出一个节点后,都是先将其左孩子入队,再将右孩子入队。因此,在队列中的每一层中,所有节点都是从左到右排序的,每一层的第一个节点就是当前层最左的节点。
在层序遍历时,将每一层的第一个节点保存到node中,当遍历结束,node就是这棵二叉树的最底层的最左边的节点。

代码

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public int findBottomLeftValue(TreeNode root) {
        Deque<TreeNode> que = new LinkedList<>();
        TreeNode node = null, tmp = null;

        que.add(root);

        do{
            // 加入分隔符
            que.add(null);

            // 当前层最左边的结点
            node = que.poll();

            if(node.left !=null){
                que.add(node.left);
            }

            if(node.right != null){
                que.add(node.right);
            }

            while((tmp = que.poll()) != null){
                if(tmp.left != null){
                    que.add(tmp.left);
                }

                if(tmp.right != null){
                    que.add(tmp.right);
                }
            }

        }while(!que.isEmpty());

        return node.val;
    }
}

112.路径总和

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。
叶子节点 是指没有子节点的节点。

112. 路径总和 - 力扣(Leetcode)

思路

要判断一条路径的总和是否为某个特定的值,需要在路径的终点即叶子节点进行判断,只要有一条路径满足条件,意味着整棵数满足条件。 对于每一个节点,

  • 如果它不是叶子节点,那么它是否满足条件取决于它的左子树或右子树是否满足条件,并且它的值为路径总和的一部分;
  • 如果它是叶子节点,判断当前节点值 + 路径当前总和 是否等于目标值。

代码

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {
        if(root == null){
            return false;
        }
        
        return traveral(root,targetSum,0);
    }

    private boolean traveral(TreeNode node, int targetSum, int curSum){
        if(node.left == null && node.right == null){
            if(curSum + node.val == targetSum){
                return true;
            }
        }

        boolean leftRes = false, rightRes = false;

        if(node.left != null){
            leftRes = traveral(node.left,targetSum,curSum + node.val);
        }

        if(node.right != null){
            rightRes = traveral(node.right,targetSum,curSum + node.val);
        }

        return leftRes || rightRes;

    }
}

113.路径总和ii

给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。
叶子节点 是指没有子节点的节点。

113. 路径总和 II - 力扣(Leetcode)

思路

使用前序遍历的遍历方式。

  • 参数:当前结点node,结果集合paths,当前路径path,目标值targetSum,当前路径上节点的总和curSum
  • 终止条件:当遍历到叶子节点时,
    • 如果curSum + node.val == targetSum,当前路径满足条件,将当前结点加入到路径中,再将本路径加入到结果集合paths中;最后,为避免影响到之后的路径,将当前节点从路径中移除
    • 否则,不满足条件,不用做任何操作。
  • 对于非叶子节点:先将当前结点加入路径,再分别沿着左子树和右子树的方向进行遍历,最后将当前节点移除出path

代码

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        List<List<Integer>> paths = new ArrayList<>();
        List<Integer> path = new ArrayList<>();

        if(root == null){
            return paths;
        }

        travesal(root,paths,path,targetSum,0);

        return paths;

    }

    private void travesal(TreeNode node,List<List<Integer>> paths,List<Integer> path,int targetSum, int curSum){
        if(node.left == null && node.right == null){
            if(curSum + node.val == targetSum){
                path.add(node.val);
                paths.add(new ArrayList(path));
                path.remove(path.size() - 1);
            }
        }

        path.add(node.val);

        if(node.left != null){
            travesal(node.left,paths,path,targetSum,curSum + node.val);
        }

        if(node.right != null){
            travesal(node.right,paths,path,targetSum,curSum + node.val);
        }

        path.remove(path.size() - 1);
    }
}

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

给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。

106. 从中序与后序遍历序列构造二叉树 - 力扣(Leetcode)

思路

已知后序遍历顺序为左右中,中序遍历为左中右,可以通过后序遍历顺序确定父结点的值,再通过该值将中序遍历的数组划分出左右子树中序遍历数组,再通过这两个数组的长度将后序遍历数组划分出左右子数的后序遍历数组。

  • 参数:中序遍历数组的左边界leftInOrder,右边界rightInOrder,后序遍历数组的左边界leftPostOrder,右边界rightPostOrder
  • 以上数组都是左闭右闭区间。即,中序遍历数组:[leftInOrder, rightInOrder],后序遍历数组:[leftPostOrder, rightPostOrder]
  • 在每一次遍历中,进行以下操作
    1. 如果leftPostOrder > rightPostOrder,这意味着后序遍历数组中没有结点,返回null
    2. 否则 ,从后序遍历数组中取出最后一个结点,这个结点就是当前子树的根节点,即postorder[rightPostOrder],存储为middle
    3. 遍历中序遍历数组,找到middle的下标delimeterIndex
    4. 通过delimeterIndex分割出左右子树的中序遍历的数组。
      • 左子树的中序遍历数组为:[leftInorder, delimeterIndex - 1];
      • 右子树的中序遍历数组为:[delimeterIndex + 1, rightInorder]
    5. 通过左右子树中序遍历数组的元素个数,划分出左右子树的后序数组。
      • 左子树的元素数量:delimeterIndex - leftInorder - 1;
      • 左子树的后序遍历数组为:[leftPostOrder, leftPostOrder + delimeterIndex - leftInorder - 1];
      • 右子树的元素数量:rightInorder - delimeterIndex - 1;
      • 右子树的后序遍历数组为:[leftPostOrder + delimeterIndex - leftInorder,rightPostOrder - 1];
    6. 创建一个节点node,值为middle
    7. node节点的左孩子:将左子树进行递归,结果作为middle节点的左孩子。
    8. node节点的右孩子:将右子树进行递归,结果作为middle节点的右孩子。
    9. 返回当前节点node

代码

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        TreeNode root = buildSubTree(0,inorder.length - 1, 0, postorder.length -1,inorder,postorder);

        return root;
    }

    // 中序: [leftInorder, rightInorder]
    // 后序: [leftPostOrder, rightPostOrder]

    private TreeNode buildSubTree(int leftInorder,int rightInorder,int leftPostOrder,int rightPostOrder,int[] inorder,int[] postorder){

        // 后序数组中没有数据了
        if(leftPostOrder>rightPostOrder){
            return null;
        }

        // 从后序中找到分割点
        int middle = postorder[rightPostOrder];

        // 分割中序数组
        // 分割后
        // 左数组:[leftInorder, delimeterIndex - 1]
        // 右数组:[delimeterIndex + 1, rightInorder]
        int delimeterIndex = leftInorder;
        for(;delimeterIndex<=rightInorder;delimeterIndex++){
            if(inorder[delimeterIndex] == middle){
                break;
            }
        }

        // 分割后序数组
        // 分割后:
        // 左数组的长度为:delimeterIndex - leftInorder -1
        // 左数组:[leftPostOrder, leftPostOrder + delimeterIndex - leftInorder - 1]
        // 右数组的长度为:rightInorder - delimeterIndex - 1
        // 右数组:[leftPostOrder + delimeterIndex - leftInorder, rightPostOrder - 1]

        TreeNode node = new TreeNode(middle);

        node.left = buildSubTree(leftInorder,delimeterIndex - 1,leftPostOrder,leftPostOrder + delimeterIndex - leftInorder - 1,inorder,postorder);

        node.right = buildSubTree(delimeterIndex + 1,rightInorder,leftPostOrder + delimeterIndex - leftInorder,rightPostOrder - 1,inorder,postorder);

        return node;

    }
}

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

给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。

105. 从前序与中序遍历序列构造二叉树 - 力扣(Leetcode)

思路

与上一题的解题思想相同,已知后序遍历顺序为左右中,前序遍历顺序为中左右,可以使用前序遍历数组替代后序遍历数组来确定子树的根的节点。

  • 参数:前序遍历数组的左边界leftPreorder,右边界rigthPreorder,中序遍历数组的左边界leftInOrder,右边界rightInOrder
  • 以上数组都是左闭右闭区间。即,中序遍历数组:[leftInOrder, rightInOrder],前序遍历数组:[leftPreorder, rigthPreorder]
  • 在每一次遍历中,进行以下操作
    1. 如果leftPreorder > rigthPreorder,这意味着后序遍历数组中没有结点,返回null
    2. 否则 ,从后序遍历数组中取出最后一个结点,这个结点就是当前子树的根节点,即preorder[leftPreorder],存储为middle
    3. 遍历中序遍历数组,找到middle的下标delimeterIndex
    4. 通过delimeterIndex分割出左右子树的中序遍历的数组。
      • 左子树的中序遍历数组为:[leftInorder, delimeterIndex - 1];
      • 右子树的中序遍历数组为:[delimeterIndex + 1, rightInorder]
    5. 通过左右子树中序遍历数组的元素个数,划分出左右子树的前序数组。
      • 左子树的元素数量:delimeterIndex - leftInorder - 1;
      • 左子树的前序遍历数组为:[leftPreorder + 1, leftPreorder + delimeterIndex - leftInorder];
      • 右子树的元素数量:rightInorder - delimeterIndex - 1;
      • 右子树的后序遍历数组为:[leftPreorder + delimeterIndex - leftInorder + 1, rigthPreorder];
    6. 创建一个节点node,值为middle
    7. node节点的左孩子:将左子树进行递归,结果作为middle节点的左孩子。
    8. node节点的右孩子:将右子树进行递归,结果作为middle节点的右孩子。
    9. 返回当前节点node

代码

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {

        return buildSubTree(0,preorder.length - 1, 0, inorder.length -1, preorder, inorder);

    }

    private TreeNode buildSubTree(int leftPreorder,int rigthPreorder,int leftInorder,int rightInorder,int[] preorder,int[] inorder){

        // 子树中已无节点
        if(leftInorder > rightInorder){
            return null;
        }

        // 取得当前子树的根节点
        int middle = preorder[leftPreorder];

        // 在中序遍历中找到根节点middle
        int delimeterIndex = leftInorder;

        for(;delimeterIndex<=rightInorder;delimeterIndex++){
            if(inorder[delimeterIndex] == middle){
                break;
            }
        }

        // 中序遍历数组划分为
        // 左子树:[leftInorder, delimeterIndex - 1]
        // 右子树:[delimeterIndex + 1, rightInorder]

        // 前序遍历数组划分为
        // 左子树: [leftPreorder + 1, leftPreorder + 1 + delimeterIndex - 1 - leftInorder]
        // 即[leftPreorder + 1, leftPreorder + delimeterIndex - leftInorder]
        // 右子树:[leftPreorder + delimeterIndex - leftInorder + 1, rigthPreorder]

        TreeNode node = new TreeNode(middle);

        node.left = buildSubTree(leftPreorder + 1, leftPreorder + delimeterIndex - leftInorder, leftInorder, delimeterIndex - 1, preorder, inorder);

         node.right = buildSubTree(leftPreorder + delimeterIndex - leftInorder + 1, rigthPreorder, delimeterIndex + 1, rightInorder, preorder, inorder);

         return node;

    }
}