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

【TomGo】二叉树递归一篇搞懂:从“会写”到“真正理解”(含全部代码+踩坑总结)

目录一、开头真实心路二、先说最核心递归三大模型 三、基础模块创建 销毁---1️⃣ 创建节点2️⃣ 销毁二叉树重点四、遍历递归入门---五、统计类加法模型六、查找类存在性七、树的高度max模型八、单值二叉树两种理解---九、结构类问题最核心---十、遍历扩展放入数组十一、最核心总结一定要记住十二、我这次真正学到的 ⚠️---一、开头真实心路大家好我是 TomGo 这段时间我把二叉树递归系统写了一遍但过程中有一个很明显的问题 ❗代码我能写出来但很多地方其实是“凭感觉写的”比如为什么销毁必须后序为什么有的地方用 有的用 ||为什么有的函数必须拆开写 这篇我把所有代码按模型彻底整理 把“为什么”讲清楚---二、先说最核心递归三大模型 我一开始是乱写的后来才发现 ❗所有二叉树递归本质就三种模型--- 1️⃣ 统计类加法模型结果 左子树 右子树 ( 当前节点)--- 2️⃣ 存在性||模型只要找到一个满足 → 就返回--- 3️⃣ 结构一致模型必须所有地方都满足--- 后面所有代码本质都在这三种里---三、基础模块创建 销毁---1️⃣ 创建节点BTNode* createNode(BTDataType x) { BTNode* newnode (BTNode*)malloc(sizeof(BTNode)); if (newnode NULL) { perror(malloc fail); exit(-1); } newnode-val x; newnode-left newnode-right NULL; return newnode; }---2️⃣ 销毁二叉树重点void freeTree(BTNode* root) { if (root NULL) return; freeTree(root-left); freeTree(root-right); free(root); }---❌ 我踩过的坑我一开始以为这样就行free(root-left);free(root-right); 这是错的--- 为什么必须递归free 只释放一个节点不会帮你释放整棵子树--- 为什么必须“后序”必须先删子节点再删自己否则会访问野指针--- ❗总结一句话销毁 后序遍历---四、遍历递归入门---前序void PreOrder(BTNode* root) { if (root NULL) { printf( N ); return; } printf( %d , root-val); PreOrder(root-left); PreOrder(root-right); }---中序void InOrder(BTNode* root) { if (root NULL) { printf( N ); return; } InOrder(root-left); printf( %d , root-val); InOrder(root-right); }---后序void PostOrder(BTNode* root) { if (root NULL) { printf( N ); return; } PostOrder(root-left); PostOrder(root-right); printf( %d , root-val); }---五、统计类加法模型 这三题本质一样---节点个数本质就是 总数 左子树 右子树 1int BinaryTreeSize(BTNode* root) { return root NULL ? 0 : BinaryTreeSize(root-left) BinaryTreeSize(root-right) 1; }---叶子节点int BinaryTreeLeafSize(BTNode* root) { if (root NULL) return 0; if (root-left NULL root-right NULL) return 1; return BinaryTreeLeafSize(root-left) BinaryTreeLeafSize(root-right); }这道题的递归过程其实可以这样理解递归会一直往下走直到走到最底层的叶子节点或者 NULL才停止。当遇到叶子节点时返回 1表示“找到一个叶子”。而对于非叶子节点它本身不是叶子它的任务只是 去左子树找叶子个数 去右子树找叶子个数最后把两边的结果加起来返回。所以整棵树的叶子个数本质就是左子树的叶子数 右子树的叶子数最终这个结果会一层一层向上返回直到回到根节点得到整棵树的叶子总数。这类问题的本质是当前节点不直接产生结果而是“汇总子树的结果”。这就是典型的“自底向上”的递归模型。第k层节点int BinaryTreeLevelKSize(BTNode* root, int k) { if (root NULL || k 0) return 0; if (k 1) return 1; return BinaryTreeLevelKSize(root-left, k - 1) BinaryTreeLevelKSize(root-right, k - 1); }--- 本质总结 ❗统计类 左 右 ( 当前)---六、查找类存在性BTNode* BinaryTreeFind(BTNode* root, BTDataType x) { if (root NULL) return NULL; if (root-val x) return root; BTNode* ret BinaryTreeFind(root-left, x); if (ret) return ret; return BinaryTreeFind(root-right, x); }这段代码的核心是先找左子树如果找到了就直接返回否则再去右子树找。具体过程是1️⃣ 先判断当前节点如果当前节点就是要找的值直接返回2️⃣ 如果不是就去左子树找如果左子树找到了ret 不为 NULL说明已经找到目标直接返回3️⃣ 如果左子树没找到ret 为 NULL才继续去右子树查找所以这里这个 if 很关键if (ret) return ret; 它的作用是一旦左子树找到了就“截断递归”避免继续往右子树查找--- 本质 ❗只要找到一个 → 返回|| 思想这段代码本质是“短路递归”左子树成功 → 直接返回左子树失败 → 才尝试右子树---七、树的高度max模型int TreeHeight(BTNode* root) { if (root NULL) return 0; int left TreeHeight(root-left); int right TreeHeight(root-right); return left right ? left 1 : right 1; }树的高度是一个“自底向上”的问题递归先计算子树高度当前节点只负责在子树结果基础上取最大值并加1我一开始以为是在“算整棵树”后来才明白每一层节点其实都只是在“接力”--- 本质高度 max(左,右) 1 注意这是“路径”不是“数量”---八、单值二叉树两种理解---❌ 写法1局部判断bool isUnivalTree(struct TreeNode* root){if (root NULL)return true;int n root-val;if (root-left root-left-val ! n)return false;if (root-right root-right-val ! n)return false;return isUnivalTree(root-left) isUnivalTree(root-right);} 但问题在于❗没有显式约束“所有节点都等于同一个值”而是靠“递归传递”去间接保证⚡ 为什么推荐写成 check(root, val)if (root-val ! val)return false; 这个是❗每个节点都直接和“统一标准”比较---✅ 写法2推荐全局判断bool check(struct TreeNode* root, int val) { if (root NULL) return true; if (root-val ! val) return false; return check(root-left, val) check(root-right, val); } bool isUnivalTree2(struct TreeNode* root) { if (root NULL) return true; return check(root, root-val); }--- 本质 ❗这是“全局一致性问题” → 用 ---九、结构类问题最核心---1️⃣ 完全相同的树isSamebool isSame(struct TreeNode* root1, struct TreeNode* root2) { if (root1 NULL root2 NULL) return true; if (root1 NULL || root2 NULL) return false; if (root1-val ! root2-val) return false; return isSame(root1-left, root2-left) isSame(root1-right, root2-right); }--- 本质 ❗必须全部一样 → 不是在“比较整棵树”而是每一对对应节点都在做“我和你一样吗”一层一层往下比只要有一个不一样 → 整体 false❌ 错误1合并判断TomGo的问题if (root1 NULL || root2 NULL || val不等) 会把NULL NULL应该 true判成 false ❌判断两棵树是否完全相同本质是递归比较对应节点如果当前节点相同并且左右子树分别完全相同则整棵树相同---2️⃣ 子树判断isContainsbool isContains(struct TreeNode* root1, struct TreeNode* root2) { if (root1 NULL) return false; if (isSame(root1, root2)) return true; return isContains(root1-left, root2) || isContains(root1-right, root2); }吧root1的所有节点都作为下一此递归的根来和root2进行IsSame验证--- 本质找位置 → ||匹配结构 → ---3️⃣ 对称二叉树bool IS(struct TreeNode* root1, struct TreeNode* root2) { if (root1 NULL root2 NULL) return true; if (root1 NULL || root2 NULL) return false; if (root1-val ! root2-val) return false; return IS(root1-left, root2-right) IS(root1-right, root2-left); }bool isSymmetric(struct TreeNode* root) 本质❗镜像左 ↔ 右十、遍历扩展放入数组void prevpush(struct TreeNode* root, int* a, int* pi) { if (root NULL) return; a[(*pi)] root-val; prevpush(root-left, a, pi); prevpush(root-right, a, pi); }因为i是输出型参数。想要改变就一定要传地址有小伙伴说前面加stack行吗但问题是多次调用之后i是之前所有调用的值的依次累加的结果这是十一、最核心总结一定要记住❗递归本质我只处理当前节点子树交给递归❗三大模型类型本质统计类左 右存在性结构一致❗关键区别 → 全部满足|| → 存在即可十二、我这次真正学到的 ⚠️我一开始以为我会递归其实只是“能写出来”当我能解释每一行代码为什么这样写的时候才算真正理解 面试会问什么为什么销毁必须后序为什么 isContains 用 ||为什么 isSame 用 为什么递归不能假设参数不为 NULL TomGo收尾递归不难难的是你以为你懂了

相关文章:

【TomGo】二叉树递归一篇搞懂:从“会写”到“真正理解”(含全部代码+踩坑总结)

目录 一、开头(真实心路) 二、先说最核心:递归三大模型 🔥 三、基础模块(创建 销毁)🌱--- 1️⃣ 创建节点 2️⃣ 销毁二叉树(重点🔥) 四、遍历&#x…...

李慕婉-仙逆-造相Z-Turbo在Linux系统上的部署教程

李慕婉-仙逆-造相Z-Turbo在Linux系统上的部署教程 专为《仙逆》粉丝打造的AI绘画模型,轻松生成李慕婉角色形象 1. 开篇:为什么选择这个模型? 如果你是个《仙逆》小说迷,或者喜欢创作动漫角色形象,那么这个模型绝对值得…...

Qwen2.5-VL-7B-Instruct视觉问答系统实战:基于Ollama的一键部署教程

Qwen2.5-VL-7B-Instruct视觉问答系统实战:基于Ollama的一键部署教程 1. 为什么你需要一个本地视觉问答系统 你有没有遇到过这样的场景:手头有一张产品说明书的扫描件,想快速提取其中的关键参数;或者收到一张包含复杂图表的财务报…...

抖音无水印下载终极指南:3分钟学会批量保存高清视频

抖音无水印下载终极指南:3分钟学会批量保存高清视频 【免费下载链接】douyin-downloader 项目地址: https://gitcode.com/GitHub_Trending/do/douyin-downloader 还在为抖音视频的水印烦恼吗?想要保存喜欢的舞蹈教学、美食教程或搞笑片段&#x…...

全局变量自加的注意点

最近在研读FreeRTOS内核源码时,被xTaskIncrementTick函数中的一段细节深深触动。这段看似冗余的代码背后,藏着嵌入式系统设计中对"绝对稳定"的极致追求。一、引发思考的代码片段在xTaskIncrementTick函数中,有这样一段关键代码&…...

FreeRTOS V8.2.1在LPC1768上的嵌入式移植与实时任务实践

1. FreeRTOS V8.2.1 在 LPC1768 平台上的嵌入式移植与工程实践FreeRTOS V8.2.1 是一个经过工业验证的轻量级实时操作系统内核,其设计哲学强调确定性、可裁剪性与硬件无关性。本版本发布于2015年,是 ARM Cortex-M3 架构(特别是 NXP LPC1768&am…...

【测试基础】06-软件测试用例设计方法之等价类

测试用例设计的方法有以下几个: 等价类边界值法场景法错误推断法因果图判定表正交实验法 本期我们先介绍等价类等价类划分法 使用场景:针对表单类页面元素测试的时候使用 典型代表: 输入框下拉列表单选复选框 概念 等价类划分法是一种典型的重…...

小型打怪游戏1.2

修改并优化了《小型打怪游戏1.1》。#include <bits/stdc.h> #include <iostream> #include <windows.h> #include <conio.h > #include <ctime> #include <cstdlib> using namespace std; char maze[15][35] {"###################&…...

2026年六西格玛管理系统选型指南:深度盘点10款高效六西格玛管理工具

在2026年数字化转型的深水区&#xff0c;企业对于质量管理的精细化要求达到了前所未有的高度&#xff0c;六西格玛管理系统已成为制造与服务行业降本增效的核心引擎。面对市场上层出不穷的六西格玛管理工具&#xff0c;如何制定一份科学的六西格玛管理系统选型指南&#xff0c;…...

通义千问3-Reranker-0.6B快速部署:低延迟(<200ms)优化技巧

通义千问3-Reranker-0.6B快速部署&#xff1a;低延迟&#xff08;<200ms&#xff09;优化技巧 1. 模型简介与核心价值 Qwen3-Reranker-0.6B 是阿里云通义千问团队专门为文本检索和排序任务设计的新一代重排序模型。这个模型的核心使命很简单&#xff1a;帮你从一堆文档中快…...

计算机CV领域一些期刊投稿,仅供参考.

顶级期刊TPAMI、TIP、都是一区CCFA,属于超难系列. AI 2区 CCFA 太难了, 其他的看图吧....

【脉宽调制DCDC功率变换学习笔记009】DCDC功率变换器建模

小信号模型是线性时不变电路模型&#xff0c;可以直接应用于所有标准电路的分析技术。为了便于建模&#xff0c;将变换器分为三个功能块&#xff1a;功率级、PWM模块和电压反馈电路。首先&#xff0c;使用各种建模技术将每个功能块转换成相应的小信号模型。三个功能块的小信号模…...

辉芒微FT60F12X单片机最小系统设计详解(无外部晶振版)

辉芒微FT60F12X单片机最小系统设计实战指南&#xff08;无外部晶振方案&#xff09; 在嵌入式硬件开发领域&#xff0c;构建稳定可靠的最小系统是每个项目的起点。辉芒微FT60F12X系列单片机以其高性价比和丰富外设资源&#xff0c;在消费电子和工业控制领域广受欢迎。本文将深入…...

YOLOv8与春联生成模型结合:智能图像识别对联生成系统

YOLOv8与春联生成模型结合&#xff1a;智能图像识别对联生成系统 用AI技术让传统春联焕发新活力&#xff0c;让每一幅对联都与你眼前的场景完美匹配 1. 项目背景与价值 春节贴春联是延续千年的传统习俗&#xff0c;但现代人常常面临一个尴尬&#xff1a;买来的春联内容千篇一律…...

Android双屏开发避坑指南:解决HDMI热插拔和屏幕适配的5个关键问题

Android双屏开发实战&#xff1a;破解HDMI热插拔与动态适配的工程难题 在商业广告机、车载中控、智能POS等场景中&#xff0c;双屏异显已成为提升用户体验的标配功能。但当工程师真正着手实现时&#xff0c;往往会遭遇HDMI热插拔引发的界面闪退、多分辨率适配失调等"暗礁&…...

Gemma-3-12b-it部署案例:智能制造工厂设备巡检图→异常检测→维修指引

Gemma-3-12b-it部署案例&#xff1a;智能制造工厂设备巡检图→异常检测→维修指引 1. 项目背景与价值 在智能制造工厂中&#xff0c;设备巡检是保障生产连续性的关键环节。传统巡检方式依赖人工记录设备状态照片&#xff0c;再由工程师分析异常并给出维修方案&#xff0c;整个…...

SAP押注“按AI用量收费”,但真正的问题不在定价,而在价值

最近一则关于sap ai定价的新闻引起了广泛关注https://www.techzine.eu/news/applications/139727/sap-moving-from-subscriptions-to-ai-use-based-pricing/这篇文章围绕SAP正在推动的一项关键转型展开&#xff1a;从传统的订阅制软件收费模式&#xff0c;转向基于AI使用量的计…...

从零到一:基于TwinCAT3的巴鲁夫IO-Link模块实战配置指南

1. 环境准备与软件安装 第一次接触TwinCAT3和巴鲁夫IO-Link模块时&#xff0c;我花了整整两天时间才搞明白环境配置的门道。现在回想起来&#xff0c;其实只要抓住几个关键点就能少走弯路。首先需要准备的是TwinCAT3 XAE开发环境&#xff0c;建议直接去倍福官网下载最新版本。安…...

Phi-3-Mini-128K在软件测试中的应用:自动化生成测试用例与报告

Phi-3-Mini-128K在软件测试中的应用&#xff1a;自动化生成测试用例与报告 最近和几个做软件测试的朋友聊天&#xff0c;发现他们每天的工作量是真不小。写测试用例、跑测试、分析日志、写报告&#xff0c;一套流程下来&#xff0c;重复性工作占了大部分时间。尤其是遇到需求变…...

窗口对象与操作

窗口对象与操作 window 是浏览器的全局对象&#xff0c;代表当前浏览器窗口。所有全局变量和函数都是 window 对象的属性和方法。获取窗口尺寸&#xff1a; console.log(window.innerWidth); // 视口宽度 console.log(window.innerHeight); // 视口高度 console.log(window.ou…...

C++20 Concepts 完全实战指南:告别 SFINAE,让模板约束更清晰

从「编译期报错 wall of text」到「简洁直观的约束表达式」&#xff0c;Concepts 是 C20 送给模板元编程开发者的最佳礼物。 引言&#xff1a;模板编程的痛点 作为 C 开发者&#xff0c;你一定经历过这样的绝望时刻&#xff1a; template<typename T> void process(T&a…...

Cronus:Arduino嵌入式I²C实时时钟多芯片统一驱动库

1. 项目概述Cronus 是一个面向嵌入式 Arduino 平台的轻量级、模块化 IC 实时时钟&#xff08;RTC&#xff09;驱动库&#xff0c;专为多型号硬件兼容性与工程可维护性而设计。其核心目标并非简单封装读写操作&#xff0c;而是构建一套统一抽象层&#xff0c;屏蔽 DS1307、DS323…...

智能体范式浅谈

这几年&#xff0c;围绕着智能体观察、思考与行动的模式&#xff0c;业内逐渐发展出了几种不同的智能体运行逻辑。而在此之前&#xff0c;即在现在较为通用的智能体逻辑模式&#xff08;我们称为智能体范式&#xff09;被总结和广泛使用之前&#xff0c;智能体如何使用则处于一…...

ComfyUI+ControlNet实战:如何用AI线稿一键生成高质量插画(附完整参数配置)

ComfyUIControlNet实战&#xff1a;从线稿到商业级插画的AI魔法 在数字艺术创作领域&#xff0c;时间成本与创意实现之间的平衡一直是困扰职业插画师的难题。传统工作流程中&#xff0c;从线稿到成稿往往需要经历数十小时的铺色、渲染和细节调整。而现在&#xff0c;ComfyUI与C…...

Cogito-V1-Preview-Llama-3B一键部署教程:Ubuntu 20.04环境快速搭建

Cogito-V1-Preview-Llama-3B一键部署教程&#xff1a;Ubuntu 20.04环境快速搭建 最近有不少朋友在问&#xff0c;有没有一个既能在本地快速跑起来&#xff0c;效果又不错的开源大模型&#xff1f;今天要聊的Cogito-V1-Preview-Llama-3B&#xff0c;我觉得是个挺有意思的选择。…...

Qwen3-TTS-12Hz-1.7B-VoiceDesign在教育领域的应用:智能语音课件生成系统

Qwen3-TTS-12Hz-1.7B-VoiceDesign在教育领域的应用&#xff1a;智能语音课件生成系统 1. 引言 想象一下&#xff0c;一位老师需要为不同年级的学生准备多语言的教学课件&#xff0c;传统的录音方式耗时耗力&#xff0c;而且很难保证发音的一致性和准确性。现在&#xff0c;借…...

Win10 安装 MySQL5.7.36 数据库记录

本文参考前文 win10安装mysql5.7 MySQL 5.7.36 国内 阿里云 下载地址 https://mirrors.aliyun.com/mysql/MySQL-5.7/mysql-5.7.36-winx64.msi 安装 mysql-5.7.36-winx64.msi 时&#xff0c;我选择的 custom 自定义安装 安装目录 D:\software\MySQL\MySQL-Server-5.7 安装完成…...

DeepSeek-OCR-2实战案例:高校教务系统成绩单PDF自动结构化入库

DeepSeek-OCR-2实战案例&#xff1a;高校教务系统成绩单PDF自动结构化入库 1. 引言&#xff1a;从堆积如山的PDF到一键入库 每到学期末&#xff0c;高校教务处的老师们就要面对一项繁重的工作&#xff1a;处理成千上万份学生成绩单PDF文件。这些文件格式各异&#xff0c;有的…...

快速入门Face3D.ai Pro:参数调优与获得最佳效果的技巧

快速入门Face3D.ai Pro&#xff1a;参数调优与获得最佳效果的技巧 关键词&#xff1a;Face3D.ai Pro、3D人脸重建、参数调优、最佳实践、UV纹理、网格细分、AI锐化 摘要&#xff1a;你已经成功部署了Face3D.ai Pro&#xff0c;但生成的效果总感觉差那么一点意思&#xff1f;别…...

One-Fox工具箱V7魔改版:从简约UI到代码透明的二次开发指南

1. One-Fox工具箱V7魔改版初体验 第一次打开One-Fox工具箱V7魔改版时&#xff0c;最直观的感受就是界面变得清爽多了。相比之前版本略显杂乱的布局&#xff0c;V7采用了极简的扁平化设计&#xff0c;所有工具图标都重新绘制过&#xff0c;配色从原来的高饱和度变成了更柔和的莫…...