力扣刷题day15|110平衡二叉树、257二叉树的所有路径、404左叶子之和

文章目录

    • 110. 平衡二叉树
      • 递归思路
      • 前序迭代思路
    • 257. 二叉树的所有路径
      • 递归思路
        • 难点
      • 迭代思路
        • 难点
    • 404.左叶子之和
      • 递归思路
        • 难点
      • 前序遍历迭代思路

110. 平衡二叉树

力扣题目链接

给定一个二叉树,判断它是否是高度平衡的二叉树。

本题中,一棵高度平衡二叉树定义为:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。

示例1:

输入:root = [3,9,20,null,null,15,7]
输出:true

示例2:

输入:root = [1,2,2,3,3,null,null,4,4]
输出:false

示例3:

输入:root = []
输出:true

递归思路

因为是要求高度,所以用后序遍历

递归三要素

  1. 明确递归函数的参数和返回值:因为要求高度差,每层返回的都要是高度,所以说int,参数是当前传入的节点
public int getHeight(TreeNode root)

如果当前传入节点为根节点的二叉树已经不是二叉平衡树了,还返回高度的话就没有意义了。

所以如果已经不是二叉平衡树了,可以返回-1 来标记已经不符合平衡树的规则了。

  1. 明确终止条件:递归的过程中依然是遇到空节点了为终止,返回0,表示当前节点为根节点的树高度为0
if (root == null) {
    return 0;
}
  1. 明确单层递归的逻辑

分别求出其左右子树的高度,然后如果差值小于等于1,则返回当前二叉树的高度,否则则返回-1,表示已经不是二叉平衡树了。

如果是-1,肯定是不平衡的了,就可以一直往上返回,不需再计算该树的其他子树的高度

// 计算左右子树的高度
int leftHeight = getHeight(root.left);
int rightHeight = getHeight(root.right);
// 如果左右子树返回的是-1的话就可以一层层返回上去
if (leftHeight == -1) {
    return -1;
}
if (rightHeight == -1) {
    return -1;
}

// 判断二者高度差
// 如果不平衡
if (Math.abs(leftHeight - rightHeight) > 1) {
    return -1;
}
// 如果平衡返回高度
return Math.max(leftHeight, rightHeight) + 1;

完整代码:

// 递归--------------------------------------------------------------
public boolean isBalanced(TreeNode root) {
    return getHeight(root) != -1;
}

public int getHeight(TreeNode root) {
    if (root == null) {
        return 0;
    }
    // 计算左右子树的高度
    int leftHeight = getHeight(root.left);
    int rightHeight = getHeight(root.right);
    // 如果左右子树返回的是-1的话就可以一层层返回上去
    if (leftHeight == -1) {
        return -1;
    }
    if (rightHeight == -1) {
        return -1;
    }

    // 判断二者高度差
    // 如果不平衡
    if (Math.abs(leftHeight - rightHeight) > 1) {
        return -1;
    }
    // 如果平衡返回高度
    return Math.max(leftHeight, rightHeight) + 1;
}
// 递归--------------------------------------------------------------

前序迭代思路

257. 二叉树的所有路径

力扣题目链接

给你一个二叉树的根节点 root ,按 任意顺序 ,返回所有从根节点到叶子节点的路径。

叶子节点 是指没有子节点的节点。

示例1:

输入:root = [1,2,3,null,5]
输出:["1->2->5","1->3"]

示例2:

输入:root = [1]
输出:["1"]

递归思路

这道题目要求从根节点到叶子的路径,所以需要前序遍历,这样才方便让父节点指向孩子节点,找到对应的路径。

递归三要素:

  1. 递归函数函数参数以及返回值:要传入根节点,记录每一条路径的path,和存放结果集的result,这里递归不需要返回值
public void traversal(TreeNode root, List<String> res, List<Integer> path)
  1. 确定递归终止条件:找到叶子节点就要结束,root左右孩子都为空的时候,就找到叶子节点。把现在存在路径的节点统一以字符串形式加上->存入res返回

为什么没有判断root是否为空呢?因为下面的逻辑可以控制空节点不入循环

if (root.left == null && root.right == null) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < path.size() - 1; i++) { // 只到倒数第二个数,因为最后一个数不带箭头
        // path的每个点存入StringBuilder,每个节点后面加箭头
        sb.append(path.get(i)).append("->");
    }
    sb.append(path.get(path.size() - 1)); // 最后一个数
    res.add(sb.toString());
    return;
}
  1. 确定单层递归逻辑:因为是前序遍历,需要先处理中间节点,中间节点就是我们要记录路径上的节点,先放进path中。
path.add(root.val);

然后是递归和回溯的过程,上面说过没有判断cur是否为空,那么在这里递归的时候,如果为空就不进行下一层递归了。所以递归前要加上判断语句,下面要递归的节点是否为空

而且递归完,要做回溯啊,因为path 不能一直加入节点,它还要删节点,然后才能加入新的节点。

if (root.left != null) {
    traversal(root.left, res, path);
    path.remove(path.size() - 1); // 回溯
}
if (root.right != null) {
    traversal(root.right, res, path);
    path.remove(path.size() - 1); // 回溯
}

注:每一次递归完,都要回溯一次

难点

    for (int i = 0; i < path.size() - 1; i++) { // 只到倒数第二个数,因为最后一个数不带箭头
        // path的每个点存入StringBuilder,每个节点后面加箭头
        sb.append(path.get(i)).append("->");
    }
    sb.append(path.get(path.size() - 1)); // 最后一个数

在将数存入path时,最后一个数字不用加箭头,因此对最后一个数单独加入,其他数和箭头一起加进path

完整代码

public List<String> binaryTreePaths(TreeNode root) {
    // 存储最后结果
    List<String> res = new ArrayList<>();
    // 存储每条边
    List<Integer> path = new ArrayList<>();
    if (root == null) {
        return res;
    }
    traversal(root, res, path);
    return res;
}

public void traversal(TreeNode root, List<String> res, List<Integer> path) {
    // 中,中为什么写在这里,因为最后一个节点也要加入到path中
    // 每遍历一个节点都要加入path
    path.add(root.val);
    // 叶子节点的左右子树肯定是空,那么就将现在的路径放回res
    if (root.left == null && root.right == null) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < path.size() - 1; i++) { // 只到倒数第二个数,因为最后一个数不带箭头
            // path的每个点存入StringBuilder,每个节点后面加箭头
            sb.append(path.get(i)).append("->");
        }
        sb.append(path.get(path.size() - 1)); // 最后一个数
        res.add(sb.toString());
        return;
    }

    if (root.left != null) {
        traversal(root.left, res, path);
        path.remove(path.size() - 1); // 回溯
    }
    if (root.right != null) {
        traversal(root.right, res, path);
        path.remove(path.size() - 1); // 回溯
    }
}

迭代思路

可以直接定义一个成员变量为object的栈Stack stack = new Stack<>();存放节点路径

难点

stack.push(root.val + "")可以直接将int类型转化为String

完整代码:

// 迭代--------------------------------------------------------------

public List<String> binaryTreePaths(TreeNode root) {
    List<String> res = new ArrayList<>();
    if (root == null) {
        return res;
    }
    Stack<Object> stack = new Stack<>();
    // 节点和路径同时入栈
    stack.push(root);
    stack.push(root.val + "");
    while (!stack.isEmpty()) {
        // 节点和路径同时出栈
        String path = (String) stack.pop();
        TreeNode node = (TreeNode) stack.pop();
        // 若找到叶子节点
        if (node.left == null && node.right == null) {
            res.add(path);
        }
        //右子节点不为空
        if (node.right != null) {
            stack.push(node.right);
            stack.push(path + "->" + node.right.val);
        }
        //左子节点不为空
        if (node.left != null) {
            stack.push(node.left);
            stack.push(path + "->" + node.left.val);
        }
    }
    return res;
}

404.左叶子之和

力扣题目链接

给定二叉树的根节点 root ,返回所有左叶子之和。

示例 1:

输入: root = [3,9,20,null,null,15,7] 
输出: 24 
解释: 在这个二叉树中,有两个左叶子,分别是 9 和 15,所以返回 24

示例 2:

输入: root = [1]
输出: 0

递归思路

左叶子的明确定义:节点A的左孩子不为空,且左孩子的左右孩子都为空(说明是叶子节点),那么A节点的左孩子为左叶子节点

难点

不能直接以叶子节点的左右为空为左节点,因为此时不能判断是左还是右孩子,而是要在他的父节点先定下他的左叶子,再进行判断

完整代码:

public int sumOfLeftLeaves(TreeNode root) {
    if (root == null) {
        return 0;
    }

    // 左
    int leftValue = sumOfLeftLeaves(root.left);
    if (root.left != null && root.left.left == null && root.left.right == null) {
        leftValue = root.left.val;
    }
    // 右
    int rightValue = sumOfLeftLeaves(root.right);
    // 中
    return leftValue + rightValue;
}

前序遍历迭代思路

前序遍历的迭代,只要把左叶子节点统计出来,就可以了

// 迭代--------------------------------------------------------------
public int sumOfLeftLeaves1(TreeNode root) {
    if (root == null) {
        return 0;
    }
    Deque<TreeNode> stack = new ArrayDeque<>();
    stack.offer(root);
    
    int sum = 0;
    while (!stack.isEmpty()) {
        TreeNode node = stack.poll();
        // 判断是叶子节点
        if (node.left != null && node.left.left == null && node.left.right ==null) {
            sum = sum + node.left.val;
        }
        if (node.left != null) {
            stack.offer(node.left);
        }
        if (node.right != null) {
            stack.offer(node.right);
        }
    }
    return sum;

}

你可能感兴趣的:(leetcode,leetcode,算法,java)