一文搞懂二叉树

一文搞懂二叉树

二叉树是计算机科学中最基础的树形数据结构,也是面试、算法开发、工程应用(如表达式解析、搜索索引)的核心考点。本文从概念→分类→存储→遍历→操作→应用层层递进,结合 C++ 代码示例,让你彻底吃透二叉树。

一、二叉树的核心定义与术语

1. 什么是二叉树?

二叉树是n(n≥0)个节点的有限集合,满足:

  • 空树(n=0);

  • 非空树:有且仅有一个根节点,其余节点分为两个互不相交的子集,分别称为左子树右子树,且左、右子树也都是二叉树。

核心约束:每个节点的子节点数量不超过2(左孩子、右孩子,顺序不能颠倒)。

2. 必备术语(看图理解更快)

假设一棵简单二叉树结构:

1 (根节点) / \ 2 3 (2是1的左孩子,3是1的右孩子;2和3互为兄弟节点) / 4 (4是2的左孩子,4是叶子节点)
术语定义
根节点树的最顶层节点,没有父节点(如节点1)
叶子节点没有左、右子节点的节点(如节点3、4)
节点的度节点拥有的子节点数(叶子节点度为0,节点1度为2,节点2度为1)
树的深度/高度从根节点到最远叶子节点的边数(上图深度为2,根节点深度为0)
根节点在第0层,其子节点在第1层,以此类推(上图节点4在第2层)
左/右子树以节点左/右孩子为根的子树(节点1的左子树是{2,4},右子树是{3})

二、二叉树的常见分类(重点区分)

根据节点的排列规则,二叉树分为普通二叉树特殊二叉树,特殊二叉树在工程中更常用。

1. 满二叉树

  • 定义:除了叶子节点,每个节点的度都为2(所有非叶子节点都有左、右两个孩子),且所有叶子节点都在同一层。

  • 特点:节点数公式2^(h+1) - 1h为树的深度),结构对称。

2. 完全二叉树

  • 定义:按层序遍历顺序给节点编号,每个节点的编号都与对应的满二叉树节点编号一致(最后一层的叶子节点靠左排列)。

  • 特点

    • 满二叉树是特殊的完全二叉树;

    • 适合用数组存储(无需额外存储指针,节省内存);

    • 父节点与子节点的索引关系:若父节点索引为i,左孩子为2i+1,右孩子为2i+2

3. 二叉搜索树(BST,Binary Search Tree)

工程中最常用的二叉树,核心是有序性

  • 定义:左子树的所有节点值< 根节点值 < 右子树的所有节点值,且左、右子树也都是二叉搜索树。

  • 核心特性中序遍历结果是严格递增的有序序列

  • 优势:查找、插入、删除的平均时间复杂度为O(logn)(类似二分查找)。

4. 平衡二叉树

  • 定义:任意节点的左、右子树的高度差的绝对值 ≤ 1(避免二叉搜索树退化成链表)。

  • 常见类型:AVL树(严格平衡)、红黑树(近似平衡,工程中更常用,如 C++map/set的底层实现)。

  • 优势:保证查找、插入、删除的最坏时间复杂度为O(logn)

三、二叉树的存储结构(C++ 实现)

二叉树有两种存储方式,分别适用于不同场景。

1. 链式存储(最常用,灵活)

指针连接节点,每个节点包含左孩子指针右孩子指针

#include <iostream> #include <vector> #include <queue> #include <memory> // 智能指针,避免内存泄漏 using namespace std; // 二叉树节点结构体(C++ 推荐用智能指针管理内存) struct TreeNode { int val; unique_ptr<TreeNode> left; // 左孩子智能指针 unique_ptr<TreeNode> right; // 右孩子智能指针 // 构造函数 TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} };

优势:支持动态插入/删除节点,适合不规则二叉树;

劣势:需要额外存储指针,内存开销略高。

2. 顺序存储(数组存储,适合完全二叉树)

数组存储节点,利用完全二叉树的索引关系定位父/子节点。

  • 根节点存在arr[0]

  • 节点arr[i]的左孩子:arr[2i+1],右孩子:arr[2i+2]

  • 节点arr[i]的父节点:arr[(i-1)/2](整数除法)。

示例:完全二叉树[1,2,3,4]的数组存储:

数组索引0123
节点值1234
优势:无需指针,内存利用率高;

劣势:非完全二叉树会浪费大量空间,不支持灵活插入/删除。

四、二叉树的核心操作:遍历(必须掌握)

遍历是指按一定顺序访问二叉树的所有节点,且每个节点仅访问一次。

二叉树的遍历分为深度优先遍历(DFS)广度优先遍历(BFS)两大类,其中 DFS 又分为 3 种顺序。

1. 深度优先遍历(DFS):递归+迭代实现

核心思想:先深后广,优先遍历子树,再遍历兄弟节点。

(1)前序遍历(根 → 左 → 右)

访问顺序:先访问根节点 → 递归遍历左子树 → 递归遍历右子树。

递归实现(最简单)

void preOrder(TreeNode* root) { if (root == nullptr) return; cout << root->val << " "; // 1. 访问根节点 preOrder(root->left.get()); // 2. 遍历左子树 preOrder(root->right.get()); // 3. 遍历右子树 }

迭代实现(用栈模拟递归,工程常用)

void preOrderIter(TreeNode* root) { if (root == nullptr) return; stack<TreeNode*> st; st.push(root); while (!st.empty()) { TreeNode* node = st.top(); st.pop(); cout << node->val << " "; // 访问根节点 // 栈是“先进后出”,先压右孩子,再压左孩子 if (node->right) st.push(node->right.get()); if (node->left) st.push(node->left.get()); } }
(2)中序遍历(左 → 根 → 右)

访问顺序:递归遍历左子树 → 访问根节点 → 递归遍历右子树。

核心特性:二叉搜索树的中序遍历结果是递增有序序列

递归实现

void inOrder(TreeNode* root) { if (root == nullptr) return; inOrder(root->left.get()); // 1. 遍历左子树 cout << root->val << " "; // 2. 访问根节点 inOrder(root->right.get()); // 3. 遍历右子树 }

迭代实现(栈+指针,关键是“左链入栈”)

void inOrderIter(TreeNode* root) { if (root == nullptr) return; stack<TreeNode*> st; TreeNode* cur = root; while (cur != nullptr || !st.empty()) { // 左链全部入栈 while (cur != nullptr) { st.push(cur); cur = cur->left.get(); } cur = st.top(); st.pop(); cout << cur->val << " "; // 访问根节点 cur = cur->right.get(); // 遍历右子树 } }
(3)后序遍历(左 → 右 → 根)

访问顺序:递归遍历左子树 → 递归遍历右子树 → 访问根节点。

递归实现

void postOrder(TreeNode* root) { if (root == nullptr) return; postOrder(root->left.get()); // 1. 遍历左子树 postOrder(root->right.get()); // 2. 遍历右子树 cout << root->val << " "; // 3. 访问根节点 }

迭代实现(标记法:区分节点是否已访问)

void postOrderIter(TreeNode* root) { if (root == nullptr) return; stack<pair<TreeNode*, bool>> st; // <节点, 是否已访问> st.push({root, false}); while (!st.empty()) { auto [node, visited] = st.top(); st.pop(); if (visited) { cout << node->val << " "; // 已访问子树,访问根节点 } else { st.push({node, true}); // 标记为待访问 // 栈先进后出:根 → 右 → 左 if (node->right) st.push({node->right.get(), false}); if (node->left) st.push({node->left.get(), false}); } } }

2. 广度优先遍历(BFS):层序遍历

核心思想:按层访问,从上到下、从左到右遍历每一层的节点。

实现工具:队列(先进先出)

代码实现

void levelOrder(TreeNode* root) { if (root == nullptr) return; queue<TreeNode*> q; q.push(root); while (!q.empty()) { int levelSize = q.size(); // 当前层的节点数 // 遍历当前层的所有节点 for (int i = 0; i < levelSize; i++) { TreeNode* node = q.front(); q.pop(); cout << node->val << " "; // 下一层节点入队 if (node->left) q.push(node->left.get()); if (node->right) q.push(node->right.get()); } cout << endl; // 每层换行 } }

遍历结果示例

对如下二叉树:

1 / \ 2 3 / 4
  • 前序遍历:1 2 4 3

  • 中序遍历:4 2 1 3

  • 后序遍历:4 2 3 1

  • 层序遍历:1 → 2 3 → 4

五、二叉搜索树(BST)的常见操作(插入/删除/查找)

二叉搜索树的有序性决定了其操作的规律,以下是核心操作的 C++ 实现。

1. 查找节点

核心逻辑:类似二分查找,根据节点值与根节点的大小关系,递归/迭代查找左/右子树。

// 查找值为target的节点,返回节点指针 TreeNode* searchBST(TreeNode* root, int target) { if (root == nullptr || root->val == target) { return root; } // 小值查左,大值查右 return target < root->val ? searchBST(root->left.get(), target) : searchBST(root->right.get(), target); }

2. 插入节点

核心逻辑

  1. 空树:直接新建节点作为根;

  2. 非空树:根据值的大小,找到左/右子树的空位置插入(不破坏有序性)。

// 插入值为val的节点,返回新树的根 unique_ptr<TreeNode> insertBST(unique_ptr<TreeNode> root, int val) { if (root == nullptr) { return make_unique<TreeNode>(val); // 空位置插入新节点 } if (val < root->val) { root->left = insertBST(move(root->left), val); // 插入左子树 } else if (val > root->val) { root->right = insertBST(move(root->right), val); // 插入右子树 } // 已存在该值,直接返回(避免重复) return root; }

3. 删除节点(最复杂,分3种情况)

核心逻辑:找到目标节点后,根据节点的子节点数量分情况处理:

  • 情况1:目标节点是叶子节点 → 直接删除;

  • 情况2:目标节点只有一个子节点 → 用子节点替换目标节点;

  • 情况3:目标节点有两个子节点 → 用右子树的最小节点(或左子树的最大节点)替换目标节点,再删除该最小节点。

// 找到右子树的最小节点(中序后继) TreeNode* findMin(TreeNode* root) { while (root->left != nullptr) { root = root->left.get(); } return root; } // 删除值为val的节点,返回新树的根 unique_ptr<TreeNode> deleteBST(unique_ptr<TreeNode> root, int val) { if (root == nullptr) return root; // 1. 找到目标节点 if (val < root->val) { root->left = deleteBST(move(root->left), val); } else if (val > root->val) { root->right = deleteBST(move(root->right), val); } else { // 2. 找到目标节点,分情况处理 // 情况1+2:无左孩子 或 无右孩子 if (root->left == nullptr) { return move(root->right); // 右孩子替换 } else if (root->right == nullptr) { return move(root->left); // 左孩子替换 } // 情况3:有两个孩子,找右子树最小节点 TreeNode* minNode = findMin(root->right.get()); root->val = minNode->val; // 替换值 // 删除右子树的最小节点 root->right = deleteBST(move(root->right), minNode->val); } return root; }

六、二叉树的工程应用场景

  1. 数据搜索与排序

    • 二叉搜索树:支持动态的插入、删除、查找,适合频繁更新的数据集;

    • 平衡二叉树(红黑树):C++std::map/std::set的底层实现,保证O(logn)时间复杂度。

  2. 表达式解析

    • 表达式树:叶子节点是操作数,非叶子节点是运算符,前序遍历对应前缀表达式,后序遍历对应后缀表达式(逆波兰表达式)。
  3. 哈夫曼树

    • 用于数据压缩(如 Huffman 编码),智驾场景中可压缩传感器采集的点云/图像数据,减少存储和传输开销。
  4. 决策树

    • 机器学习中的分类模型,每个非叶子节点是一个特征判断,叶子节点是分类结果,用于目标检测的后处理分类。
  5. 路径规划

    • 树形结构的遍历思想可用于智驾中的局部路径搜索,如 A* 算法的状态树遍历。

七、核心总结

  1. 遍历是二叉树的基础:前序/中序/后序(DFS)、层序(BFS)的递归+迭代实现必须熟练,中序遍历是二叉搜索树的关键。

  2. 二叉搜索树的核心是有序性:中序遍历有序,插入/删除/查找都是基于有序性的二分思想。

  3. 工程中优先用平衡二叉树:避免二叉搜索树退化成链表(时间复杂度退化为O(n))。

  4. C++ 实现注意内存管理:推荐用unique_ptr/shared_ptr管理节点,避免野指针和内存泄漏。

八、实战小练习

  1. 用本文的代码,构建一棵二叉搜索树[5,3,7,2,4,6,8],分别输出前序、中序、后序、层序遍历结果;

  2. 删除节点3,再输出中序遍历结果,验证是否仍有序。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1210461.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JavaScript数组(Array对象)应用介绍

.一、Array数组基础 在JavaScript中,数组没有原始类型,可以使用Array对象,Array对象具有以下特征: 1)可调整大小的,并且可以包含不同的数据类型。 2)不是关联数组,因此,不能使用任意字符串作为索引访问数组元…

一文搞懂C++容器篇

一文搞懂C之容器篇 C STL&#xff08;标准模板库&#xff09;的核心价值之一就是提供了丰富的容器——即“存储数据的通用结构”&#xff0c;能帮我们快速实现数据的存储、访问、插入、删除等操作&#xff0c;无需重复造轮子。本文将C常用容器按“功能分类”梳理&#xff0c;从…

2026雅思网上辅导口碑排行榜:五大机构深度测评及高分提分方案解析

结合2026年雅思考试命题趋势与全国教育机构调研数据,本次针对雅思网上辅导开展全面深度测评,聚焦提分效果、个性化方案、性价比等核心维度,梳理出靠谱实用的机构排行榜。雅思备考中,考生常面临基础薄弱无从下手、单…

全国雅思培训机构深度测评TOP5:权威榜单助力高效选课,精准提分不踩坑

雅思培训选课是众多考生及家长的核心难题,尤其北京海淀区、上海徐汇区、广州天河区、深圳南山区、成都锦江区等热门留学区县考生,如何筛选优质靠谱的雅思教育机构,掌握实用提分技巧、实现高分目标,是备考首要阻碍。…

2026年安徽民办技校格局:谁在定义“靠谱”的新标准?

一、 核心结论 在安徽省职业教育蓬勃发展的浪潮中,“靠谱”已成为学生与家长择校的核心诉求。它不再局限于传统的就业安置,而是深度融合了高技能培养质量、前瞻性专业布局、多元化升学通道以及坚实的办学底蕴。基于这…

2026雅思网课权威口牌测评排行榜:高分提分方案实用解析推荐

据2026年雅思考试行业权威调研显示,考生在雅思培训选课过程中,常面临优质教育机构筛选难、高分提分技巧匮乏、个性化方案缺失、性价比权衡迷茫等痛点。为帮助考生精准避开误区,本次基于全维度测评体系,结合机构资质…

广州大健康食品OEM工厂推荐:广东诺品健康,一站式营养健康解决方案服务商

广州大健康食品OEM工厂推荐:广东诺品健康,一站式营养健康解决方案服务商 一、为什么选择广州大健康食品OEM工厂? 随着大健康产业的蓬勃发展(2024年国内大健康代加工市场规模突破3800亿元,同比增长22%),越来越多…

2026雅思网上辅导权威靠谱测评排行榜:高分提分机构深度解析

结合2026年雅思考试命题趋势与全国教育机构调研数据,本次针对雅思网上辅导开展全面深度测评,聚焦提分效果、个性化方案、性价比等核心维度,梳理出靠谱实用的机构排行榜。雅思备考中,考生常面临基础薄弱无从下手、单…

CF2106D Flower Boy

https://codeforces.com/problemset/problem/2106/D 解题思路:我们不光要求前缀,还要求后缀。我们可以用前后缀分别来维护a[N]大于b[N]的数目,到时候这届如果前缀数不够的话我们就直接拼起来看行不行。 #include<…

安装ubuntu系统所遇到的各种各样的问题,并附有安装无线网卡,显卡驱动,以及桌面系统教程

1、安装ubuntu系统 这个网上的教程很多,随便找一个教程就好 大致的过程就是,准备u盘,制作u盘(可以用rufus 然后进入bio界面 关闭安全启动,这一步非常关键,没有这一步,接下来的许多问题都无法解决 直接UEFI驱动,…

2026雅思培训在线课程权威靠谱测评排行榜 高分提分方案深度解析

在雅思考试竞争日趋激烈的当下,挑选靠谱的教育机构、获取优质提分技巧、制定个性化实用方案,成为众多考生及家长的核心痛点。市面上雅思培训课程良莠不齐,选课过程中既要兼顾性价比与提分效果,又要甄别机构资质与教…

2026雅思托福培训机构口碑排行榜 权威深度解析测评高分提分方案

随着2026年留学与职场竞争加剧,雅思考试已成为衡量国际语言能力的核心指标,然而考生在雅思培训选课过程中,常面临优质机构筛选难、提分技巧不系统、个性化方案缺失等痛点。如何在海量教育机构中锁定靠谱选择,凭借高…

2026雅思托福培训机构靠谱口碑排行榜 权威深度测评优选方案

基于2026年雅思考试趋势、全国万余名考生反馈及行业师资调研,结合权威教研标准与实战提分数据,我们开展了全面的雅思托福培训机构深度测评,旨在破解考生选课难题。雅思备考中,多数人面临培训机构鱼龙混杂、提分效果…

2026 雅思培训在线课程 靠谱口碑排名榜深度测评推荐解析

随着2026年雅思机考占比持续攀升,评分标准更侧重综合能力,考生在雅思培训选课过程中面临诸多困境:基础薄弱不知如何起步、分数卡在瓶颈难以突破、优质教育机构鱼龙混杂、缺乏个性化提分方案等。多数考生渴望通过权威…

2026 雅思培训在线课程 权威深度测评靠谱口碑排行榜推荐

结合2026年雅思考试趋势与行业调研数据,雅思培训选课市场乱象频发,多数考生面临优质教育机构筛选难、提分技巧匮乏、个性化方案缺失等问题。为助力考生高效突破考试瓶颈、冲刺高分,本文联合教育大数据研究院,通过全…

7.DNS的定义和由来

1、DNS的定义和由来 以上仅供参考,如有疑问,留言联系

2026雅思托福培训机构高分提分权威测评:口碑排行榜解析

基于2026年雅思托福考试改革趋势与行业调研数据,结合万千考生及家长诉求,本次针对市面上主流教育机构开展全面深度测评,聚焦优质课程、高分提分技巧、个性化方案等核心维度,梳理出靠谱实用的机构排行榜。在雅思培训…

2026雅思托福培训机构口碑排行榜 权威深度测评高分提分方案

在雅思培训赛道中,考生常面临选课迷茫、提分乏力、优质教育机构甄别困难等核心痛点,既要兼顾性价比与提分效果,又需寻求权威个性化方案,精准掌握考试技巧实现高分突破。基于2026年雅思考试趋势与全行业深度测评,本…

搜索与回溯算法专题--子集和排列的枚举

枚举排列: 不重集排列: 思路: 用cur表示以cur为当前位置填元素 当cur==n时排列生成完成,输出 然后枚举b数组,尝试在cur上填每一个数 填之前检查一下当前要填的数与a数组之前的数是否相同,不相同代表排列合法,继…

0 基础小白如何快速入门网络安全?这份指南帮你少走弯路

0 基础小白如何快速入门网络安全&#xff1f;这份指南帮你少走弯路 一、为什么要学网络安全&#xff1f; 在互联网时代&#xff0c;网络安全早已不是 “黑客” 的专属领域。从大学生的个人信息保护&#xff0c;到企业的数据安全&#xff0c;甚至国家的网络主权&#xff0c;都离…