0到1算法之路 daDay 15 二叉树part 03

Day 15 二叉树part 03

222. 完全二叉树的节点个数

一般的二叉树记录节点个数的方法

java 复制代码
private static int countNodes(TreeNode root) {
        if (root == null) return 0;
        int leftHeight = countNodes(root.left);
        int rightHeight = countNodes(root.right);
        int height = leftHeight + rightHeight + 1;  //左子树的节点+右子树的节点 +1(本身的节点)
        return height;
}

利用完全二叉树的特性

一个树(即使他不是满二叉树)其左右子树(持续向下遍历)出来比如是满二叉树,而满二叉树的节点数量为2^n -1

我们两种返回条件

  1. 遇到null返回0
  2. 遇到该子树为满二叉树就返回 该子数的节点数量

否则我们就继续递归左右子数,再求和返回节点数量

while循环,同一出发点,从左子树的左子树递归,从右子树的右子树递归,若两高度相等,就说明是满二叉树(两端不为null,中间的节点是不可能为null的,因为是完全二叉树,从左到右排列),我们这个时候就之间返回节点数量。

若不为满二叉树,我们就递归左、右子树,再相加+1返回即可

java 复制代码
 private static int countNodes(TreeNode root) {
        if (root == null) return 0;
        TreeNode left = root.left;
        int leftHeight = 0;
        TreeNode right = root.right;
        int rightHeight = 0;
        while (left != null) {
            left = left.left;
            leftHeight++;
        }
        while (right != null) {
            right = right.right;
            rightHeight++;
        }
        if (leftHeight == rightHeight) {
            return (2 << leftHeight) - 1;
        }
        int leftNum = countNodes(root.left);
        int rightNum = countNodes(root.right);
        return leftNum + rightNum + 1;  //返回当前节点
    }

错误点:

java 复制代码
        TreeNode left = root;
        TreeNode right = root;

以上是错误的,假设这棵子树的高度为3,我就干脆得到它的高度

但是我们后面计算子树节点的时候,是利用移位公式return (2 << leftHeight) - 1;

2向左移位1是4,移位0才是2, 我当时误以为2<<1还是本身,当作2^1来看了,我们求出的数据应该是要比深度小1才是

java 复制代码
        TreeNode left = root.left;
        TreeNode right = root.right;

110. 平衡二叉树

求深度用先序 从表层逐个往下,遇到就加1

求高度用后序 因为要遍历完之后,加上一些值之后再返回

将左右子树的高度差返回,不符合就返回-1,左右子数高度返回回来,记得判断一下是否为-1,大问题拆解为小问题

注意此处是差值是绝对值,这样无论是左右哪个大都可以应对自如了

java 复制代码
private static boolean isBalanced(TreeNode root) {
        if (getHeight(root) != -1) return true;
        return false;
    }

    private static int getHeight(TreeNode root) {
        if (root == null) return 0;
        int leftHeight = getHeight(root.left);
        if (leftHeight == -1) return -1;//由于是循环调用,-1也作为一个标记,遇到直接返回
        int rightHeight = getHeight(root.right);
        if (rightHeight == -1) return -1;
        // 左右子树高度相减绝对值左右子树高度差大于1,return -1表示已经不是平衡树了
        if (Math.abs(leftHeight - rightHeight) > 1) {
            return -1; //用-1去标记不平衡
        }
        return Math.max(leftHeight, rightHeight) + 1;//加1是加上本身的长度
    }

错误

  1. 未递归检查所有子节点的平衡性
  2. 调用isBalanced时,重复调用isHeight函数,递归应当就写在一个函数当中,主函数就单纯调用函数即可,这样也暴露了函数的细节
java 复制代码
private static boolean isBalanced(TreeNode root) {
        if (root == null) return true;
        int leftHeight = isHeight(root.left);
        int rightHeight = isHeight(root.right);
        if (Math.abs(leftHeight - rightHeight) > 1) return false;
        return true;
    }

    private static int isHeight(TreeNode root) {
        if (root == null) return 0;
        int leftHeight = isHeight(root.left);
        int rightHeight = isHeight(root.right);
        return Math.max(leftHeight, rightHeight) + 1;
    }

257. 二叉树的所有路径

递归内部其实都有回溯,只是将其隐藏了

中是处理的过程(路径收集),处理一定要放在判断是否为叶子节点之前,

StringBuilder sb = new StringBuilder();// StringBuilder用来拼接字符串,速度更快,由于线程不安全,建议不涉及线程的时候使用

java 复制代码
            for (int i = 0; i < paths.size() - 1; i++) {
                sb.append(paths.get(i)).append("->");
            }
            sb.append(paths.get(paths.size() - 1));// 记录最后一个节点,路径符号不在最后一位

这里我们,我们直接弹出最后一个元素,remove可以根据下标和值弹出

java 复制代码
if (root.left != null) {
            getPath(root.left, path, node);
            node.remove(node.size() - 1);
        }

一次递归调用中最多只进行一次收集,我们遇到叶子结点就正式将收集的元素拼接在一起,其余都是收集元素,收集的时候注意不要用错,我们节点的收集是node,我们一到叶子节点,我们就收集,集合起来是返回path。

java 复制代码
private static List<String> binaryTreePaths(TreeNode root) {
        if (root == null) return Collections.emptyList();
        List<String> path = new ArrayList<>();
        ArrayList<Integer> node = new ArrayList<>();
        getPath(root, path, node);
        return path;
    }

    private static void getPath(TreeNode root, List<String> path, ArrayList<Integer> node) {
        node.add(root.val);//node而不是path
        if (root.left == null && root.right == null) {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < node.size() - 1; i++) {
                sb.append(node.get(i)).append("->");
            }
            sb.append(node.get(node.size() - 1));
            path.add(sb.toString());
        }
        if (root.left != null) {
            getPath(root.left, path, node);
            node.remove(node.size() - 1);
        }

        if (root.right != null) {
            getPath(root.right, path, node);
            node.remove(node.size() - 1);
        }
    }

问题1:

因为这里我们的函数public void getPath(TreeNode root, List list, ArrayList node)已经将参数传递进来,我们就可以进行调用 ,这里其实和 c++一样,我们这里传入的是引用数据类型,背后是地址传输

404. 左叶子之和

整体概括:

我们就是要求左子树的左叶子和右子树的左叶子

我知道如何判断叶子节点,但是父节点的左孩子该如何判断呢? 我们遍历到这个节点,不知道这个节点是否是左叶子。我们遍历到父元素的时候,就先对它左孩子做判断:左孩子的左右子树是否为空,是叶子节点就是我们所要找的。

我们这里可以采用后序遍历,收集本节点的,返回当前这个节点的:左子树左子树的左叶子之和 以及 右子树的左叶子之和。后序遍历,下向上返回。

整体概括:

我们就是要求左子树的左叶子和右子树的左叶子。

(root.left!=null&&root.left.leftnull&&root.left.rightnull)这里的逻辑是,先判断左孩子是否为空,才有资格调用左孩子的左右孩子,不然会爆出空指针异常。 第一则判断是左节点不为空,而后两则是判断 这个是否为叶子节点。

问题:

我们不知道当前遍历到的节点是否为左孩子节点(虽然可以知道是否是子节点)。

java 复制代码
private static int sumOfLeftLeaves(TreeNode root) {
        if (root == null) return 0;
        if (root.left == null && root.right == null) return 0;//就返回,但不知道是否是左孩子
        int leftSum = sumOfLeftLeaves(root.left);
        int rightSum = sumOfLeftLeaves(root.right);
        int mid = 0;
        //这个判断是什么意思    判断左子树不为空,并且判断是否为叶子节点
        if (root.left != null && root.left.left == null && root.left.right == null) {
            mid = root.left.val;  //注意是root.left.val 而不是 root.val
        }
        return mid + leftSum + rightSum;
}
相关推荐
JK0x072 小时前
代码随想录算法训练营 Day58 图论Ⅷ 拓扑排序 Dijkstra
android·算法·图论
oioihoii3 小时前
C++23 <spanstream>:基于 std::span 的高效字符串流处理
c++·算法·c++23
白熊1883 小时前
【机器学习基础】机器学习入门核心算法:朴素贝叶斯(Naive Bayes)
人工智能·算法·机器学习
Owen_Q3 小时前
AtCoder Beginner Contest 407
开发语言·c++·算法
客卿1233 小时前
力扣100题---字母异位词分组
算法·leetcode·职场和发展
JK0x073 小时前
代码随想录算法训练营 Day56 图论Ⅶ 最小生成树算法 Prim Kruskal
算法·图论
javthon实验室4 小时前
【Prompt Engineering】摸索出的一些小套路
算法·prompt
山楂树の4 小时前
Three.js 直线拐角自动圆角化(圆弧转弯)
算法·3d·webgl
苏荷水7 小时前
day12 leetcode-hot100-19(矩阵2)
算法·leetcode·矩阵
之之为知知7 小时前
数学笔记三:特殊矩阵
笔记·学习·线性代数·算法·职场和发展·矩阵·职场发展