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;
}
相关推荐
满分观察网友z7 分钟前
从UI噩梦到导航之梦:一道LeetCode经典题如何拯救了我的项目(116. 填充每个节点的下一个右侧节点指针)
算法
DoraBigHead1 小时前
小哆啦解题记——两数失踪事件
前端·算法·面试
不太可爱的大白1 小时前
Mysql分片:一致性哈希算法
数据库·mysql·算法·哈希算法
Tiandaren5 小时前
Selenium 4 教程:自动化 WebDriver 管理与 Cookie 提取 || 用于解决chromedriver版本不匹配问题
selenium·测试工具·算法·自动化
岁忧6 小时前
(LeetCode 面试经典 150 题 ) 11. 盛最多水的容器 (贪心+双指针)
java·c++·算法·leetcode·面试·go
chao_7896 小时前
二分查找篇——搜索旋转排序数组【LeetCode】两次二分查找
开发语言·数据结构·python·算法·leetcode
秋说8 小时前
【PTA数据结构 | C语言版】一元多项式求导
c语言·数据结构·算法
Maybyy8 小时前
力扣61.旋转链表
算法·leetcode·链表
卡卡卡卡罗特10 小时前
每日mysql
数据结构·算法
chao_78911 小时前
二分查找篇——搜索旋转排序数组【LeetCode】一次二分查找
数据结构·python·算法·leetcode·二分查找