当前位置: 首页 > news >正文

代码随想录Day15:二叉树(平衡二叉树、二叉树的所有路径、左叶子之和、完全二叉树的节点个数——全递归版本)

一、实战

110平衡二叉树

110. 平衡二叉树 - 力扣(LeetCode)

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

确定遍历顺序:求高度一定用后序遍历

递归三步曲分析:

1.明确递归函数的参数和返回值。参数:当前传入节点。 返回值:以当前传入节点为根节点的树的高度。

那么如何标记左右子树是否差值大于1呢?如果当前传入节点为根节点的二叉树已经不是二叉平衡树了,还返回高度的话就没有意义了,可以返回-1 来标记已经不符合平衡树的规则了。

2.明确终止条件。递归的过程中依然是遇到空节点了为终止,返回0,表示当前节点为根节点的树高度为0

3.明确单层递归的逻辑。如何判断以当前传入节点为根节点的二叉树是否是平衡二叉树呢?当然是其左子树高度和其右子树高度的差值。

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

package org.example.Tree;public class isBalanced110 {public boolean isBalanced(TreeNode root) {if(root==null)return true;int result=getDeep(root);return result != -1;}private int getDeep(TreeNode root) {//终止条件if(root==null)return 0;//单层逻辑int left=getDeep(root.left);//leftif(left==-1)return -1;int right=getDeep(root.right);//rightif(right==-1)return -1;if(Math.abs(left-right)>1)return -1;//rootelse return Math.max(left,right)+1;}public static void main(String[] args) {}
}

257二叉树的所有路径

257. 二叉树的所有路径 - 力扣(LeetCode)

思路:确定遍历顺序——前序遍历。因为只有前序遍历才能让父节点指向子节点从而输出合格的路径以及实现回溯。

递归三部曲:

1.递归函数参数以及返回值。要传入根节点,记录每一条路径的path,和存放结果集的result,这里递归不需要返回值

2.确定递归终止条件。

因为本题要找到叶子节点,就开始结束的处理逻辑了(把路径放进result里)。那么什么时候算是找到了叶子节点? 是当 cur不为空,其左右孩子都为空的时候,就找到叶子节点。

3.确定单层递归逻辑

因为是前序遍历,需要先处理中间节点,中间节点就是我们要记录路径上的节点,先放进path中。然后是递归和回溯的过程,上面说过没有判断cur是否为空,那么在这里递归的时候,如果为空就不进行下一层递归了。此时还没完,递归完,要做回溯啊,因为path 不能一直加入节点,它还要删节点,然后才能加入新的节点。我们知道,回溯和递归是一一对应的,有一个递归,就要有一个回溯,这么写的话相当于把递归和回溯拆开了, 一个在花括号里,一个在花括号外。所以回溯要和递归永远在一起,世界上最遥远的距离是你在花括号里,而我在花括号外!

为什么前序遍历递归的终止条件是叶子节点,后序遍历一般是到叶子节点下一步?通义大模型-参考

        在树的遍历中,不论是前序遍历、中序遍历还是后序遍历,递归终止条件的主要目的是确定何时停止递归调用。这个终止条件通常是检查当前节点是否为null,即该位置没有子节点存在,而不是特别针对叶子节点或进一步到null。

        对于前序遍历(访问顺序为:根 -> 左 -> 右),它首先处理的是当前节点(“根”)。如果当前节点是叶子节点(没有左孩子和右孩子的节点),那么它的左右递归调用将直接因为检测到null而返回,不会进行更多的处理。因此,虽然表面上看起来像是在叶子节点终止,实际上是由于叶子节点的孩子都是null,从而导致递归终止。

        对于后序遍历(访问顺序为:左 -> 右 -> 根),逻辑上需要先完成左右子树的遍历才能处理当前节点。这意味着即使当前节点是叶子节点,其左右子节点(即null)也需要被递归函数检查一遍,以确保遵循正确的遍历顺序。这也就是为什么有时候会感觉到后序遍历是在叶子节点的下一步(即null)才真正停止的原因。

简而言之,无论是前序遍历还是后序遍历,递归的终止条件都是遇到null(即不存在的子节点)。区别在于它们处理节点的时机不同,前序遍历在第一次遇到节点时就处理,而后序遍历则要等到左右子树都处理完毕之后再处理当前节点。

package org.example.Tree;import java.util.ArrayList;
import java.util.List;//递归法
public class binaryTreePaths257 {public List<String> binaryTreePaths(TreeNode root) {List<String> result=new ArrayList<>();//存最终的结果if (root == null) return result;List<Integer> paths = new ArrayList<>();//作为结果中的路径traversal(root, paths, result);return result;}private void traversal(TreeNode root, List<Integer> paths, List<String> result) {paths.add(root.val);//根节点// 遇到叶子结点if (root.left == null && root.right == null) {StringBuilder sb = new StringBuilder();//StringBuilder可变字符串,用来拼接字符串,速度更快for (int i = 0; i < paths.size() - 1; i++) {sb.append(paths.get(i)).append("->");}sb.append(paths.get(paths.size() - 1));result.add(sb.toString());//收集一个路径return;}//递归和回溯是同时进行,所以要放在同一个花括号里if (root.left != null) { // 左traversal(root.left, paths, result);paths.remove(paths.size() - 1);// 回溯}if (root.right != null) { // 右traversal(root.right, paths, result);paths.remove(paths.size() - 1);// 回溯}}
}

404左叶子之和

404. 左叶子之和 - 力扣(LeetCode)

思路:首先要注意是判断左叶子,不是二叉树左侧节点,所以不要上来想着层序遍历。是父节点的左孩子并且为叶子结点

递归方法想的话很明显就是后序遍历,因为最后是需要一层一层把节点之和输上去。其他方法也可以,但是后序会在思路和代码上更简洁。

那么判断当前节点是不是左叶子是无法判断的,必须要通过节点的父节点来判断其左孩子是不是左叶子。如果该节点的左节点不为空,该节点的左节点的左节点为空,该节点的左节点的右节点为空,则找到了一个左叶子

package org.example.Tree;public class sumOfLeftLeaves404 {public int sumOfLeftLeaves(TreeNode root) {int sum=0;if (root == null) return 0;int left = sumOfLeftLeaves(root.left);    // 左int right = sumOfLeftLeaves(root.right);  // 右//判断是否为左叶子(父节点来判断)if (root.left != null && root.left.left == null && root.left.right == null) {sum+=root.left.val;}sum+=left+right;return sum;}
}

222完全二叉树的节点个数

222. 完全二叉树的节点个数 - 力扣(LeetCode)

思路:

普通二叉树——递归方法想的话很明显就是后序遍历,因为最后是需要一层一层把节点个数结果输上去。

完全二叉树——在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2^(h-1) 个节点。

这里关键在于如何去判断一个左子树或者右子树是不是满二叉树呢?在完全二叉树中,如果递归向左遍历的深度等于递归向右遍历的深度,那说明就是满二叉树。

通用后序遍历

public int countNodes(TreeNode root) {if(root == null) {return 0;}return countNodes(root.left) + countNodes(root.right) + 1;}

完全二叉树特性

package org.example.Tree;public class countNodes222 {public int countNodes(TreeNode root) {if (root == null) return 0;TreeNode left = root.left;TreeNode right = root.right;int leftDepth = 1, rightDepth = 1; //这里初始为0是有目的的,为了下面求指数方便while (left != null) {  // 求左子树深度left = left.left;leftDepth++;}while (right != null) { // 求右子树深度right = right.right;rightDepth++;}if (leftDepth == rightDepth) {return (int) (Math.pow(2,leftDepth) - 1);}return countNodes(root.left) + countNodes(root.right) + 1;}
}
http://www.dtcms.com/a/271527.html

相关文章:

  • 记忆管理框架MemOS——在时序推理上较OpenAI提升159%
  • python+vue的企业产品订单管理系统
  • pytorch常用API
  • [特殊字符] 突破小样本瓶颈:DataDream——用Stable Diffusion生成高质量分类数据集
  • 认证鉴权技术解析:COOKIE | SESSION | TOKEN | JWT | SSO
  • `fatal: bad config value for ‘color.ui‘`错误解决方案
  • 前端UI逻辑复杂可以用什么设计模式
  • 卫星通信终端天线的5种对星模式之二:功率检测型载波跟踪
  • 在Excel用公式计算周次
  • NumPy-梯度与导数计算详解
  • 用一个代码案例详解介绍vmalloc函数的功能和作用
  • 权限分级看板管理:实时数据驱动决策的关键安全基石
  • 奇异值分解(singular value decomposition,SVD)
  • 笔试——Day2
  • 单细胞入门(2)-经典案例分析
  • EPLAN 电气制图(六):结构盒与设备管理器核心概念(基础知识选看)
  • 脑电分析入门指南:信号处理、特征提取与机器学习
  • python 在运行时没有加载修改后的版本
  • windows server2019安全修复
  • 数据结构——深度优先搜索与广度优先搜索的实现
  • STM32-待机唤醒实验
  • 学习笔记(30):matplotlib绘制简单图表-绘制正弦波
  • Python的标准库之时间库(小白五分钟从入门到精通)
  • 【Netty+WebSocket详解】WebSocket全双工通信与Netty的高效结合与实战
  • 循环神经网络详解
  • cherryStudio electron因为环境问题无法安装解决方法或打包失败解决方法
  • NLP自然语言处理04 transformer架构模拟实现
  • Git版本控制完全指南:从入门到实战(简单版)
  • 【02】MFC入门到精通——MFC 手动添加创建新的对话框模板
  • 【PyTorch】PyTorch中torch.nn模块的全连接层