代码随想录-Day17 | LeetCode110. 平衡二叉树、LeetCode257. 二叉树的所有路径、LeetCode404.左叶子之和

文档讲解: 代码随想录
视频讲解: 《代码随想录》算法公开课-跟着Carl学算法

LeetCode110. 平衡二叉树

题目链接:110. 平衡二叉树

题目描述:给定一个二叉树,判断它是否是高度平衡的二叉树(一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1)。

回顾:高度是从叶子节点开始向上层层遍历加1,所以对于求高度采用后序遍历左右中:先遍历左右孩子,然后返回给中间节点1)而深度是从根节点开始向下层层加1,所以对于求深度采用前序遍历中左右:从中间节点向下去遍历节点,统计节点的深度,而不是向上去返回)
思路:了解上面思路之后本题求解非常简单清晰(递归法):

  1. 确定传入参数和返回值:定义求节点高度的函数,其形参只需传入根节点root,返回值我们表示每个节点的高度,所以用int表示。
  2. 终止条件:递归遍历到空节点时,表明当前空节点的高度为0,所以直接return 0;
  3. 单层循环逻辑:我们按照后续遍历的思路,先遍历每个节点的左子节点,对应的高度值为多少,如果为-1,则表示在这之前的递归中已经确定了某个位置的左右高度差大于1!能够确保当前二叉树一定不平衡!所以向上直接返回-1,不用再继续进行后面求高度值的语句;右子节点同理;最后对于中间节点,会根据左右子树的高度差是否大于1来判断是否平衡,若小于等于1则返回左右子树的最大值然后加1。
class Solution {
    public boolean isBalanced(TreeNode root) {
        // 后序遍历:左右中 根据左右子树节点的高度差来判断是否平衡
        // 若符合要求 则将根据左右子树的高度最大值+1来获取中间节点的高度值
        int res = getHeight(root);
        if (res == -1) {
            return false;
        } else {
            return true;
        }

    }

    // 定义求节点高度的函数:传入参数为node 返回值int类型
    public int getHeight(TreeNode node) {
        // 终止条件:当遍历到空节点时 返回0
        if (node == null) {
            return 0;
        }
        // 后序遍历:左右中
        // 1.递归遍历当前节点的左子节点
        int leftHeight = getHeight(node.left);
        // 如果左子节点返回的高度值为-1 就说明不是二叉树 向上传递-1
        if (leftHeight == -1) {
            return -1;
        }
        // 2.递归遍历当前节点的右子节点
        int rightHeight = getHeight(node.right);
        // 同理如果右子节点返回的高度值为-1 就说明不是二叉树 向上传递-1
        if (rightHeight == -1) {
            return -1;
        }
        // 3.遍历中间节点:根据左右子树的高度差来判断 如果符合则向上返回节点高度
        if (Math.abs(leftHeight - rightHeight) > 1) {
            return -1;
        } else {
            return Math.max(leftHeight, rightHeight) + 1;
        }

    }
}

LeetCode257. 二叉树的所有路径

题目链接:257. 二叉树的所有路径

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

思路:不得不感叹递归思想实在是妙!题目要求返回所有从根节点到叶子节点的完整路径,通过分析应该用前序遍历更合适。我们用一个容器保存一个完整路径元素。同时里面需要用到回溯算法的思想,只要是为了保证在得到一个完整路径之后,把除了根节点以外的其他节点从容器中弹出,然后递归遍历其他路径,很明显这个容器元素弹出的规则是后进先出,所以用栈作为存储容器更合适!下面从递归三要素起分析整个代码过程:

  1. 传入参数和返回值:首先根节点是一定要用的,同时还有我们刚刚说的用于保存一个完整路径元素的栈;由于我们最终遍历完所有路径也需要保存并输出,所以我们还需要传入存储最终结果的链表
  2. 终止条件:当从根节点按照左右顺序不断递归遍历到叶子节点时,就证明了已经有完整的路径。所以我们的终止条件可以直接在叶子节点上设置,而不是以往做题时遍历到空节点才终止;一旦遍历到叶子节点,我们就把栈中里的元素按题目要求(元素中间用->)依次拼接成字符串,然后把字符串送给链表作为一个完整路径的结果。
  3. 单层递归逻辑(前序遍历:根左右):1.对于中间节点:我们只需要把当前所遍历的节点压栈即可。但有一个注意点:我们的终止条件是在叶子节点上截止,如果压栈的语句写到终止条件的后面,就会造成叶子节点没有进栈就已经输出结果了,显然不对,所以说压栈的语句需要写在终止条件之前!2.处理左子节点:如果某个节点的左子节点不为空,就进行递归遍历,同时在遍历语句实现回溯,元素从栈中弹出,也就是说当栈中元素已经有一个完整路径之后,通过回溯算法从栈中除根节点外全部从栈中弹出,然后按照同样的递归遍历其他路径。3.处理右子节点与处理左子节点的流程一样,不再过多赘述。
class Solution {
    public List<String> binaryTreePaths(TreeNode root) {
        // 由于要求从根节点到叶子节点的路径
        // 递归方法选择前序遍历:中左右
        Stack<Integer> stack = new Stack<>();
        List<String> result = new ArrayList<>();
        backtracking(root, stack, result);
        return result;
    }

    // 创建递归遍历和回溯函数
    // 传入参数:根节点,栈(回溯时要求后进先出),存储最终结果数组链表
    public void backtracking(TreeNode node, Stack<Integer> stack, List<String> result) {
        // 处理中间节点:由于终止条件是判断叶子节点,若写到终止条件下面,叶子节点不会进栈
        stack.push(node.val);
        // 终止条件:当遍历到叶子结点时就证明已经有完整的路径 此时将stack的数据添加到result
        if (node.left == null && node.right == null) {
            // 每弹出一个元素就传递给stringBuilder进行后拼接
            StringBuilder sb = new StringBuilder();
            for (Integer i : stack) {
                sb.append(i + "->");
            }
            result.add(sb.toString().substring(0, sb.length() - 2)); // 将字符串添加到结果链表中
        }
        // 左子节点:如果不为空进行递归,然后回溯
        if (node.left != null) {
            backtracking(node.left, stack, result);
            stack.pop();
        }

        // 右子节点:同理如果不为空进行递归,然后回溯
        if (node.right != null) {
            backtracking(node.right, stack, result);
            stack.pop();
        }

    }
}

LeetCode404.左叶子之和

题目链接:404.左叶子之和

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

思路:首先考虑怎么判断一个节点是左叶子节点?想不清楚这一点该题目很难求解。首先它作为叶子节点要满足:左右孩子都为空;同时它要作为一个叶子节点,还有必须满足了一点是:它是它父节点的左孩子。看起来可能有点绕,如果理解透彻,归根结底就是**叶子节点本身判断不了自己是否为左叶子节点,需要通过叶子节点的父节点来判断!**在代码中左叶子需要满足的条件可总结为:

  • 某节点的左子节点不为空&&该节点的左孙子节点为空&&该节点的右孙子节点也为空

然后思考选择什么遍历方式?由于需要通过递归遍历不断向上返回左叶子节点的和,所以首选后序遍历(需要向上返回的通常采用左右中后续遍历方式)。明白如何判断左子节点以及确定递归遍历方式之后代码就很简洁,但要注意求和的过程,包括三个变量:左子树求和值+右子树求和值+当前节点左叶子节点的值

class Solution {
    public int sumOfLeftLeaves(TreeNode root) {
        //2.终止条件:初步考虑遍历到空节点截止
        if (root == null) {
            return 0;
        }
        // 3.单层递归逻辑:后续:左右中
        // 左右子节点
        int leftSum = sumOfLeftLeaves(root.left);
        int rightSum = sumOfLeftLeaves(root.right);
        // 中间节点
        int midSum = 0;
        if (root.left != null && root.left.left == null && root.left.right == null) {
            midSum = root.left.val;
        }
        int sum = leftSum + rightSum + midSum;
        return sum;

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值