🎯 Java 二叉树详解
📌 二叉树是数据结构中「承上启下」的核心结构
它是递归思想 最好的练习场,也是 Map / Set / 堆 / 平衡树 / B 树的底层基础
本文系统整理树 与二叉树 的概念、性质、存储、遍历与基本操作,末尾附 14 道经典 OJ 面试题的完整思路与代码
📖 本文导读
本文将系统讲解 树型结构 与 二叉树:
- 树:基本概念、相关术语、表示形式、应用场景
- 二叉树:概念、两种特殊二叉树、5 条重要性质、顺序/链式存储
- 基本操作:前序/中序/后序/层序遍历、求结点个数、求高度、判断完全二叉树等
- 面试题练习:14 道力扣/牛客经典题,含思路分析与完整 Java 代码
适合 Java 数据结构初学者阅读,也适合作为面试前的复习清单
### 文章目录
- [🎯 Java 二叉树详解](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [📖 本文导读](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [@toc](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [1️⃣ 树型结构(了解)](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [1.1 什么是树?](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [1.2 树的相关概念(重要)](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [1.3 树的表示形式(了解)](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [1.4 树的应用](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [2️⃣ 二叉树(重点)](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [2.1 概念](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [2.2 两种特殊的二叉树](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [2.3 二叉树的性质](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [2.4 性质练习题](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [📝 参考答案与解析](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [2.5 二叉树的存储](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [3️⃣ 二叉树的基本操作](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [3.1 前置说明](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [3.2 二叉树的遍历](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [3.2.1 什么是遍历?](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [3.2.2 前、中、后序遍历(递归实现)](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [3.2.3 层序遍历](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [3.2.4 遍历练习题](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [📝 参考答案与解析](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [3.3 二叉树的其他基本操作](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [4️⃣ 二叉树相关 OJ 面试题](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [4.1 检查两棵树是否相同](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [4.2 另一棵树的子树](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [4.3 翻转二叉树](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [4.4 判断一棵二叉树是否是平衡二叉树](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [4.5 对称二叉树](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [4.6 二叉树的构建及遍历](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [4.7 二叉树的分层遍历](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [4.8 二叉树的最近公共祖先](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [4.9 根据前序与中序遍历构造二叉树](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [4.10 根据中序与后序遍历构造二叉树](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [4.11 二叉树创建字符串](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [4.12 二叉树前序非递归遍历实现](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [4.13 二叉树中序非递归遍历实现](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [4.14 二叉树后序非递归遍历实现](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [📊 知识图谱总结](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [📊 小结](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
- [✍️ 写在最后](#文章目录 🎯 Java 二叉树详解 📖 本文导读 @[toc] 1️⃣ 树型结构(了解) 1.1 什么是树? 1.2 树的相关概念(重要) 1.3 树的表示形式(了解) 1.4 树的应用 2️⃣ 二叉树(重点) 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 性质练习题 📝 参考答案与解析 2.5 二叉树的存储 3️⃣ 二叉树的基本操作 3.1 前置说明 3.2 二叉树的遍历 3.2.1 什么是遍历? 3.2.2 前、中、后序遍历(递归实现) 3.2.3 层序遍历 3.2.4 遍历练习题 📝 参考答案与解析 3.3 二叉树的其他基本操作 4️⃣ 二叉树相关 OJ 面试题 4.1 检查两棵树是否相同 4.2 另一棵树的子树 4.3 翻转二叉树 4.4 判断一棵二叉树是否是平衡二叉树 4.5 对称二叉树 4.6 二叉树的构建及遍历 4.7 二叉树的分层遍历 4.8 二叉树的最近公共祖先 4.9 根据前序与中序遍历构造二叉树 4.10 根据中序与后序遍历构造二叉树 4.11 二叉树创建字符串 4.12 二叉树前序非递归遍历实现 4.13 二叉树中序非递归遍历实现 4.14 二叉树后序非递归遍历实现 📊 知识图谱总结 📊 小结 ✍️ 写在最后)
1️⃣ 树型结构(了解)
1.1 什么是树?
树 是一种非线性的数据结构,它是由 n(n >= 0)个有限结点组成的一个具有层次关系的集合
把它叫做树,是因为它看起来像一棵倒挂的树------根朝上,叶朝下
树具有以下特点:
- 有一个特殊的结点,称为根结点 ,根结点没有前驱结点
- 除根结点外,其余结点被分成 M(M > 0)个互不相交的集合 T1、T2、......、Tm,其中每一个集合 Ti 又是一棵与树类似的子树 ;每棵子树的根结点有且只有一个前驱,可以有 0 个或多个后继
- 树是递归定义的
⚠️ 注意
树形结构中,子树之间不能有交集,否则就不是树形结构
下面的例子可以帮你判断「树与非树」:
- ✅ 子树是不相交的
- ✅ 除了根结点外,每个结点有且仅有一个父结点
- ✅ 一棵 N 个结点的树有 N - 1 条边
例如下图,把 D 同时也接到 C 的下面,D 就有了两个父结点,子树产生了交集,它就不再是一棵树:
#mermaid-svg-LE7IrWpknT9qkFNK{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-LE7IrWpknT9qkFNK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-LE7IrWpknT9qkFNK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-LE7IrWpknT9qkFNK .error-icon{fill:#552222;}#mermaid-svg-LE7IrWpknT9qkFNK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-LE7IrWpknT9qkFNK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-LE7IrWpknT9qkFNK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-LE7IrWpknT9qkFNK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-LE7IrWpknT9qkFNK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-LE7IrWpknT9qkFNK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-LE7IrWpknT9qkFNK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-LE7IrWpknT9qkFNK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-LE7IrWpknT9qkFNK .marker.cross{stroke:#333333;}#mermaid-svg-LE7IrWpknT9qkFNK svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-LE7IrWpknT9qkFNK p{margin:0;}#mermaid-svg-LE7IrWpknT9qkFNK .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-LE7IrWpknT9qkFNK .cluster-label text{fill:#333;}#mermaid-svg-LE7IrWpknT9qkFNK .cluster-label span{color:#333;}#mermaid-svg-LE7IrWpknT9qkFNK .cluster-label span p{background-color:transparent;}#mermaid-svg-LE7IrWpknT9qkFNK .label text,#mermaid-svg-LE7IrWpknT9qkFNK span{fill:#333;color:#333;}#mermaid-svg-LE7IrWpknT9qkFNK .node rect,#mermaid-svg-LE7IrWpknT9qkFNK .node circle,#mermaid-svg-LE7IrWpknT9qkFNK .node ellipse,#mermaid-svg-LE7IrWpknT9qkFNK .node polygon,#mermaid-svg-LE7IrWpknT9qkFNK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-LE7IrWpknT9qkFNK .rough-node .label text,#mermaid-svg-LE7IrWpknT9qkFNK .node .label text,#mermaid-svg-LE7IrWpknT9qkFNK .image-shape .label,#mermaid-svg-LE7IrWpknT9qkFNK .icon-shape .label{text-anchor:middle;}#mermaid-svg-LE7IrWpknT9qkFNK .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-LE7IrWpknT9qkFNK .rough-node .label,#mermaid-svg-LE7IrWpknT9qkFNK .node .label,#mermaid-svg-LE7IrWpknT9qkFNK .image-shape .label,#mermaid-svg-LE7IrWpknT9qkFNK .icon-shape .label{text-align:center;}#mermaid-svg-LE7IrWpknT9qkFNK .node.clickable{cursor:pointer;}#mermaid-svg-LE7IrWpknT9qkFNK .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-LE7IrWpknT9qkFNK .arrowheadPath{fill:#333333;}#mermaid-svg-LE7IrWpknT9qkFNK .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-LE7IrWpknT9qkFNK .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-LE7IrWpknT9qkFNK .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-LE7IrWpknT9qkFNK .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-LE7IrWpknT9qkFNK .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-LE7IrWpknT9qkFNK .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-LE7IrWpknT9qkFNK .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-LE7IrWpknT9qkFNK .cluster text{fill:#333;}#mermaid-svg-LE7IrWpknT9qkFNK .cluster span{color:#333;}#mermaid-svg-LE7IrWpknT9qkFNK div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-LE7IrWpknT9qkFNK .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-LE7IrWpknT9qkFNK rect.text{fill:none;stroke-width:0;}#mermaid-svg-LE7IrWpknT9qkFNK .icon-shape,#mermaid-svg-LE7IrWpknT9qkFNK .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-LE7IrWpknT9qkFNK .icon-shape p,#mermaid-svg-LE7IrWpknT9qkFNK .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-LE7IrWpknT9qkFNK .icon-shape .label rect,#mermaid-svg-LE7IrWpknT9qkFNK .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-LE7IrWpknT9qkFNK .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-LE7IrWpknT9qkFNK .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-LE7IrWpknT9qkFNK :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} A
B
C
D
E
F
G
H
1.2 树的相关概念(重要)
先看一棵示例树,后面的概念都围绕它来讲:
#mermaid-svg-BXc9h8XoZ2Uecoij{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-BXc9h8XoZ2Uecoij .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-BXc9h8XoZ2Uecoij .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-BXc9h8XoZ2Uecoij .error-icon{fill:#552222;}#mermaid-svg-BXc9h8XoZ2Uecoij .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-BXc9h8XoZ2Uecoij .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-BXc9h8XoZ2Uecoij .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-BXc9h8XoZ2Uecoij .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-BXc9h8XoZ2Uecoij .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-BXc9h8XoZ2Uecoij .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-BXc9h8XoZ2Uecoij .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-BXc9h8XoZ2Uecoij .marker{fill:#333333;stroke:#333333;}#mermaid-svg-BXc9h8XoZ2Uecoij .marker.cross{stroke:#333333;}#mermaid-svg-BXc9h8XoZ2Uecoij svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-BXc9h8XoZ2Uecoij p{margin:0;}#mermaid-svg-BXc9h8XoZ2Uecoij .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-BXc9h8XoZ2Uecoij .cluster-label text{fill:#333;}#mermaid-svg-BXc9h8XoZ2Uecoij .cluster-label span{color:#333;}#mermaid-svg-BXc9h8XoZ2Uecoij .cluster-label span p{background-color:transparent;}#mermaid-svg-BXc9h8XoZ2Uecoij .label text,#mermaid-svg-BXc9h8XoZ2Uecoij span{fill:#333;color:#333;}#mermaid-svg-BXc9h8XoZ2Uecoij .node rect,#mermaid-svg-BXc9h8XoZ2Uecoij .node circle,#mermaid-svg-BXc9h8XoZ2Uecoij .node ellipse,#mermaid-svg-BXc9h8XoZ2Uecoij .node polygon,#mermaid-svg-BXc9h8XoZ2Uecoij .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-BXc9h8XoZ2Uecoij .rough-node .label text,#mermaid-svg-BXc9h8XoZ2Uecoij .node .label text,#mermaid-svg-BXc9h8XoZ2Uecoij .image-shape .label,#mermaid-svg-BXc9h8XoZ2Uecoij .icon-shape .label{text-anchor:middle;}#mermaid-svg-BXc9h8XoZ2Uecoij .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-BXc9h8XoZ2Uecoij .rough-node .label,#mermaid-svg-BXc9h8XoZ2Uecoij .node .label,#mermaid-svg-BXc9h8XoZ2Uecoij .image-shape .label,#mermaid-svg-BXc9h8XoZ2Uecoij .icon-shape .label{text-align:center;}#mermaid-svg-BXc9h8XoZ2Uecoij .node.clickable{cursor:pointer;}#mermaid-svg-BXc9h8XoZ2Uecoij .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-BXc9h8XoZ2Uecoij .arrowheadPath{fill:#333333;}#mermaid-svg-BXc9h8XoZ2Uecoij .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-BXc9h8XoZ2Uecoij .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-BXc9h8XoZ2Uecoij .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BXc9h8XoZ2Uecoij .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-BXc9h8XoZ2Uecoij .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BXc9h8XoZ2Uecoij .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-BXc9h8XoZ2Uecoij .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-BXc9h8XoZ2Uecoij .cluster text{fill:#333;}#mermaid-svg-BXc9h8XoZ2Uecoij .cluster span{color:#333;}#mermaid-svg-BXc9h8XoZ2Uecoij div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-BXc9h8XoZ2Uecoij .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-BXc9h8XoZ2Uecoij rect.text{fill:none;stroke-width:0;}#mermaid-svg-BXc9h8XoZ2Uecoij .icon-shape,#mermaid-svg-BXc9h8XoZ2Uecoij .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BXc9h8XoZ2Uecoij .icon-shape p,#mermaid-svg-BXc9h8XoZ2Uecoij .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-BXc9h8XoZ2Uecoij .icon-shape .label rect,#mermaid-svg-BXc9h8XoZ2Uecoij .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BXc9h8XoZ2Uecoij .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-BXc9h8XoZ2Uecoij .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-BXc9h8XoZ2Uecoij :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} A
B
C
D
E
F
G
H
I
J
K
L
M
N
P
Q
核心概念
- 结点的度:一个结点含有子树的个数称为该结点的度;上图:A 的度为 6,B 的度为 2,D 的度为 0
- 树的度 :一棵树中,所有结点度的最大值称为树的度;上图:树的度为 6
- 叶子结点或终端结点:度为 0 的结点称为叶结点;上图:H、I、K、L、M、N、P、Q、D、G 等结点为叶结点
- 双亲结点或父结点:若一个结点含有子结点,则这个结点称为其子结点的父结点;上图:A 是 B 的父结点
- 孩子结点或子结点:一个结点含有的子树的根结点称为该结点的子结点;上图:B 是 A 的孩子结点
- 根结点:一棵树中,没有双亲结点的结点;上图:A
层次相关
- 结点的层次:从根开始定义起,根为第 1 层,根的子结点为第 2 层,以此类推
- 树的高度或深度 :树中结点的最大层次;上图:树的高度为 4(A→B→J→P)
以下概念只需了解,知道是什么意思即可:
- 非终端结点或分支结点:度不为 0 的结点;上图:A、B、C、E、F、J 等结点为分支结点
- 兄弟结点:具有相同父结点的结点互称为兄弟结点;上图:B、C 是兄弟结点
- 堂兄弟结点:双亲在同一层的结点互为堂兄弟;上图:H、I 互为堂兄弟结点
- 结点的祖先:从根到该结点所经分支上的所有结点;上图:A 是所有结点的祖先
- 子孙:以某结点为根的子树中任一结点都称为该结点的子孙;上图:所有结点都是 A 的子孙
- 森林:由 m(m >= 0)棵互不相交的树组成的集合称为森林
💡 一句话记忆
「度」看下面有几个孩子 ,「层次」看离根有多远 ,
n0 = n2 + 1(叶子比双分支结点多一个)
1.3 树的表示形式(了解)
树结构相对线性表就比较复杂了,要存储表示起来也比较麻烦。实际中树有很多种表示方式:
| 表示法 | 思路 |
|---|---|
| 双亲表示法 | 每个结点记录父结点的下标 |
| 孩子表示法 | 每个结点记录所有孩子的引用(或链表) |
| 孩子双亲表示法 | 同时记录孩子和父结点 |
| 孩子兄弟表示法 | 只记录第一个孩子 和下一个兄弟 |
这里简单了解其中最常用的孩子兄弟表示法:
java
class Node {
int value; // 树中存储的数据
Node firstChild; // 第一个孩子引用
Node nextBrother; // 下一个兄弟引用
}
它的巧妙之处在于:任意一棵树都能转换成二叉树------把「孩子」当成「左孩子」,把「兄弟」当成「右孩子」即可
1.4 树的应用
树在计算机世界里随处可见,最典型的就是文件系统管理(目录和文件):
/
├── etc
├── bin
├── home
│ ├── dmsai
│ ├── arod
│ │ ├── bashrc
│ │ └── profile
│ ├── damon
│ └── wong
│ └── mydata
└── usr
其他常见应用:
- 编译器的语法树(AST)
- 数据库的索引结构(B 树 / B+ 树)
- 网络中的路由表
- 游戏中的行为树 、AI 的决策树
2️⃣ 二叉树(重点)
2.1 概念
一棵二叉树 是结点的一个有限集合,该集合:
- 或者为空
- 或者是由一个根节点 加上两棵别称为左子树 和右子树的二叉树组成
#mermaid-svg-oxDtclSgCfdX3YnZ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-oxDtclSgCfdX3YnZ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-oxDtclSgCfdX3YnZ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-oxDtclSgCfdX3YnZ .error-icon{fill:#552222;}#mermaid-svg-oxDtclSgCfdX3YnZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-oxDtclSgCfdX3YnZ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-oxDtclSgCfdX3YnZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-oxDtclSgCfdX3YnZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-oxDtclSgCfdX3YnZ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-oxDtclSgCfdX3YnZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-oxDtclSgCfdX3YnZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-oxDtclSgCfdX3YnZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-oxDtclSgCfdX3YnZ .marker.cross{stroke:#333333;}#mermaid-svg-oxDtclSgCfdX3YnZ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-oxDtclSgCfdX3YnZ p{margin:0;}#mermaid-svg-oxDtclSgCfdX3YnZ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-oxDtclSgCfdX3YnZ .cluster-label text{fill:#333;}#mermaid-svg-oxDtclSgCfdX3YnZ .cluster-label span{color:#333;}#mermaid-svg-oxDtclSgCfdX3YnZ .cluster-label span p{background-color:transparent;}#mermaid-svg-oxDtclSgCfdX3YnZ .label text,#mermaid-svg-oxDtclSgCfdX3YnZ span{fill:#333;color:#333;}#mermaid-svg-oxDtclSgCfdX3YnZ .node rect,#mermaid-svg-oxDtclSgCfdX3YnZ .node circle,#mermaid-svg-oxDtclSgCfdX3YnZ .node ellipse,#mermaid-svg-oxDtclSgCfdX3YnZ .node polygon,#mermaid-svg-oxDtclSgCfdX3YnZ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-oxDtclSgCfdX3YnZ .rough-node .label text,#mermaid-svg-oxDtclSgCfdX3YnZ .node .label text,#mermaid-svg-oxDtclSgCfdX3YnZ .image-shape .label,#mermaid-svg-oxDtclSgCfdX3YnZ .icon-shape .label{text-anchor:middle;}#mermaid-svg-oxDtclSgCfdX3YnZ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-oxDtclSgCfdX3YnZ .rough-node .label,#mermaid-svg-oxDtclSgCfdX3YnZ .node .label,#mermaid-svg-oxDtclSgCfdX3YnZ .image-shape .label,#mermaid-svg-oxDtclSgCfdX3YnZ .icon-shape .label{text-align:center;}#mermaid-svg-oxDtclSgCfdX3YnZ .node.clickable{cursor:pointer;}#mermaid-svg-oxDtclSgCfdX3YnZ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-oxDtclSgCfdX3YnZ .arrowheadPath{fill:#333333;}#mermaid-svg-oxDtclSgCfdX3YnZ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-oxDtclSgCfdX3YnZ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-oxDtclSgCfdX3YnZ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oxDtclSgCfdX3YnZ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-oxDtclSgCfdX3YnZ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oxDtclSgCfdX3YnZ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-oxDtclSgCfdX3YnZ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-oxDtclSgCfdX3YnZ .cluster text{fill:#333;}#mermaid-svg-oxDtclSgCfdX3YnZ .cluster span{color:#333;}#mermaid-svg-oxDtclSgCfdX3YnZ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-oxDtclSgCfdX3YnZ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-oxDtclSgCfdX3YnZ rect.text{fill:none;stroke-width:0;}#mermaid-svg-oxDtclSgCfdX3YnZ .icon-shape,#mermaid-svg-oxDtclSgCfdX3YnZ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oxDtclSgCfdX3YnZ .icon-shape p,#mermaid-svg-oxDtclSgCfdX3YnZ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-oxDtclSgCfdX3YnZ .icon-shape .label rect,#mermaid-svg-oxDtclSgCfdX3YnZ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oxDtclSgCfdX3YnZ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-oxDtclSgCfdX3YnZ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-oxDtclSgCfdX3YnZ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 1
2
4
3
5
6
从上图可以看出:
- 二叉树不存在度大于 2 的结点
- 二叉树的子树有左右之分,次序不能颠倒 ,因此二叉树是有序树
⚠️ 注意
对于任意的二叉树,都是由以下几种情况复合而成的
空树 只有根结点 只有左子树 只有右子树 左右子树均存在
NULL A A A A
/ \ / \
B B B C
正因为二叉树是递归定义 的,所以后面几乎所有操作都可以用递归来写
2.2 两种特殊的二叉树
1. 满二叉树
一棵二叉树,如果每一层的结点数都达到最大值,则这棵二叉树就是满二叉树
也就是说,如果一棵二叉树的层数为 K,且结点总数是 2^K - 1,则它就是满二叉树
#mermaid-svg-0Gaph8pOTdqZeOOW{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-0Gaph8pOTdqZeOOW .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-0Gaph8pOTdqZeOOW .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-0Gaph8pOTdqZeOOW .error-icon{fill:#552222;}#mermaid-svg-0Gaph8pOTdqZeOOW .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-0Gaph8pOTdqZeOOW .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-0Gaph8pOTdqZeOOW .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-0Gaph8pOTdqZeOOW .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-0Gaph8pOTdqZeOOW .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-0Gaph8pOTdqZeOOW .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-0Gaph8pOTdqZeOOW .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-0Gaph8pOTdqZeOOW .marker{fill:#333333;stroke:#333333;}#mermaid-svg-0Gaph8pOTdqZeOOW .marker.cross{stroke:#333333;}#mermaid-svg-0Gaph8pOTdqZeOOW svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-0Gaph8pOTdqZeOOW p{margin:0;}#mermaid-svg-0Gaph8pOTdqZeOOW .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-0Gaph8pOTdqZeOOW .cluster-label text{fill:#333;}#mermaid-svg-0Gaph8pOTdqZeOOW .cluster-label span{color:#333;}#mermaid-svg-0Gaph8pOTdqZeOOW .cluster-label span p{background-color:transparent;}#mermaid-svg-0Gaph8pOTdqZeOOW .label text,#mermaid-svg-0Gaph8pOTdqZeOOW span{fill:#333;color:#333;}#mermaid-svg-0Gaph8pOTdqZeOOW .node rect,#mermaid-svg-0Gaph8pOTdqZeOOW .node circle,#mermaid-svg-0Gaph8pOTdqZeOOW .node ellipse,#mermaid-svg-0Gaph8pOTdqZeOOW .node polygon,#mermaid-svg-0Gaph8pOTdqZeOOW .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-0Gaph8pOTdqZeOOW .rough-node .label text,#mermaid-svg-0Gaph8pOTdqZeOOW .node .label text,#mermaid-svg-0Gaph8pOTdqZeOOW .image-shape .label,#mermaid-svg-0Gaph8pOTdqZeOOW .icon-shape .label{text-anchor:middle;}#mermaid-svg-0Gaph8pOTdqZeOOW .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-0Gaph8pOTdqZeOOW .rough-node .label,#mermaid-svg-0Gaph8pOTdqZeOOW .node .label,#mermaid-svg-0Gaph8pOTdqZeOOW .image-shape .label,#mermaid-svg-0Gaph8pOTdqZeOOW .icon-shape .label{text-align:center;}#mermaid-svg-0Gaph8pOTdqZeOOW .node.clickable{cursor:pointer;}#mermaid-svg-0Gaph8pOTdqZeOOW .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-0Gaph8pOTdqZeOOW .arrowheadPath{fill:#333333;}#mermaid-svg-0Gaph8pOTdqZeOOW .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-0Gaph8pOTdqZeOOW .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-0Gaph8pOTdqZeOOW .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0Gaph8pOTdqZeOOW .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-0Gaph8pOTdqZeOOW .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0Gaph8pOTdqZeOOW .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-0Gaph8pOTdqZeOOW .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-0Gaph8pOTdqZeOOW .cluster text{fill:#333;}#mermaid-svg-0Gaph8pOTdqZeOOW .cluster span{color:#333;}#mermaid-svg-0Gaph8pOTdqZeOOW div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-0Gaph8pOTdqZeOOW .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-0Gaph8pOTdqZeOOW rect.text{fill:none;stroke-width:0;}#mermaid-svg-0Gaph8pOTdqZeOOW .icon-shape,#mermaid-svg-0Gaph8pOTdqZeOOW .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0Gaph8pOTdqZeOOW .icon-shape p,#mermaid-svg-0Gaph8pOTdqZeOOW .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-0Gaph8pOTdqZeOOW .icon-shape .label rect,#mermaid-svg-0Gaph8pOTdqZeOOW .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0Gaph8pOTdqZeOOW .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-0Gaph8pOTdqZeOOW .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-0Gaph8pOTdqZeOOW :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 1
2
3
4
5
6
7
上图是一棵 3 层的满二叉树,结点总数 2³ - 1 = 7
2. 完全二叉树
完全二叉树是效率很高的数据结构,它是由满二叉树引出来的
对于深度为 K 的、有 n 个结点的二叉树,当且仅当其每一个结点都与深度为 K 的满二叉树中编号从 0 至 n - 1 的结点一一对应时,称之为完全二叉树
#mermaid-svg-nUijtZUowRUXA1f6{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-nUijtZUowRUXA1f6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-nUijtZUowRUXA1f6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-nUijtZUowRUXA1f6 .error-icon{fill:#552222;}#mermaid-svg-nUijtZUowRUXA1f6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-nUijtZUowRUXA1f6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-nUijtZUowRUXA1f6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-nUijtZUowRUXA1f6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-nUijtZUowRUXA1f6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-nUijtZUowRUXA1f6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-nUijtZUowRUXA1f6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-nUijtZUowRUXA1f6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-nUijtZUowRUXA1f6 .marker.cross{stroke:#333333;}#mermaid-svg-nUijtZUowRUXA1f6 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-nUijtZUowRUXA1f6 p{margin:0;}#mermaid-svg-nUijtZUowRUXA1f6 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-nUijtZUowRUXA1f6 .cluster-label text{fill:#333;}#mermaid-svg-nUijtZUowRUXA1f6 .cluster-label span{color:#333;}#mermaid-svg-nUijtZUowRUXA1f6 .cluster-label span p{background-color:transparent;}#mermaid-svg-nUijtZUowRUXA1f6 .label text,#mermaid-svg-nUijtZUowRUXA1f6 span{fill:#333;color:#333;}#mermaid-svg-nUijtZUowRUXA1f6 .node rect,#mermaid-svg-nUijtZUowRUXA1f6 .node circle,#mermaid-svg-nUijtZUowRUXA1f6 .node ellipse,#mermaid-svg-nUijtZUowRUXA1f6 .node polygon,#mermaid-svg-nUijtZUowRUXA1f6 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-nUijtZUowRUXA1f6 .rough-node .label text,#mermaid-svg-nUijtZUowRUXA1f6 .node .label text,#mermaid-svg-nUijtZUowRUXA1f6 .image-shape .label,#mermaid-svg-nUijtZUowRUXA1f6 .icon-shape .label{text-anchor:middle;}#mermaid-svg-nUijtZUowRUXA1f6 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-nUijtZUowRUXA1f6 .rough-node .label,#mermaid-svg-nUijtZUowRUXA1f6 .node .label,#mermaid-svg-nUijtZUowRUXA1f6 .image-shape .label,#mermaid-svg-nUijtZUowRUXA1f6 .icon-shape .label{text-align:center;}#mermaid-svg-nUijtZUowRUXA1f6 .node.clickable{cursor:pointer;}#mermaid-svg-nUijtZUowRUXA1f6 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-nUijtZUowRUXA1f6 .arrowheadPath{fill:#333333;}#mermaid-svg-nUijtZUowRUXA1f6 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-nUijtZUowRUXA1f6 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-nUijtZUowRUXA1f6 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-nUijtZUowRUXA1f6 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-nUijtZUowRUXA1f6 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-nUijtZUowRUXA1f6 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-nUijtZUowRUXA1f6 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-nUijtZUowRUXA1f6 .cluster text{fill:#333;}#mermaid-svg-nUijtZUowRUXA1f6 .cluster span{color:#333;}#mermaid-svg-nUijtZUowRUXA1f6 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-nUijtZUowRUXA1f6 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-nUijtZUowRUXA1f6 rect.text{fill:none;stroke-width:0;}#mermaid-svg-nUijtZUowRUXA1f6 .icon-shape,#mermaid-svg-nUijtZUowRUXA1f6 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-nUijtZUowRUXA1f6 .icon-shape p,#mermaid-svg-nUijtZUowRUXA1f6 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-nUijtZUowRUXA1f6 .icon-shape .label rect,#mermaid-svg-nUijtZUowRUXA1f6 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-nUijtZUowRUXA1f6 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-nUijtZUowRUXA1f6 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-nUijtZUowRUXA1f6 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 1
2
3
4
5
6
🔑 性质
满二叉树是一种特殊的完全二叉树,反之不成立
完全二叉树的特点是:结点自上而下、自左至右依次排列,中间不能有空缺
2.3 二叉树的性质
- 若规定根结点的层数为 1,则一棵非空二叉树的第 i 层 上最多有 2 i − 1 2^{i-1} 2i−1(i > 0)个结点
- 若规定只有根结点的二叉树的深度为 1,则深度为 K 的二叉树的最大结点数是 2 K − 1 2^K - 1 2K−1(K >= 0)
- 对任何一棵二叉树,如果其叶结点个数为 n0 ,度为 2 的非叶结点个数为 n2 ,则有 n0 = n2 + 1
- 具有 n 个结点的完全二叉树的深度 k 为 ⌈ log 2 ( n + 1 ) ⌉ \lceil \log_2(n+1) \rceil ⌈log2(n+1)⌉(上取整)
- 对于具有 n 个结点的完全二叉树,如果按照从上至下、从左至右 的顺序对所有节点从 0 开始编号,则对于序号为 i 的结点有:
- 若 i > 0,双亲序号:(i - 1) / 2;i = 0,i 为根结点编号,无双亲结点
- 若 2i + 1 < n,左孩子序号:2i + 1,否则无左孩子
- 若 2i + 2 < n,右孩子序号:2i + 2,否则无右孩子
💡 性质 3 推导过程
设二叉树结点总数为 n,度为 0、1、2 的结点数分别为 n0、n1、n2,则:
① 结点总数:
n = n0 + n1 + n2② 边数:除根结点外,每个结点都有一条来自父结点的边,所以
边数 = n - 1又因为度为 1 的结点贡献 1 条向下的边、度为 2 的结点贡献 2 条,所以
边数 = n1 + 2 * n2联立可得:
n0 + n1 + n2 - 1 = n1 + 2 * n2,整理得 n0 = n2 + 1
💡 性质 5 有什么用?它就是堆(优先级队列)用数组存储完全二叉树的依据:不需要存指针,靠下标运算就能找到父结点和左右孩子
2.4 性质练习题
1. 某二叉树共有 399 个结点,其中有 199 个度为 2 的结点,则该二叉树中的叶子结点数为( )
A 不存在这样的二叉树 B 200 C 198 D 199
2. 在具有 2n 个结点的完全二叉树中,叶子结点个数为( )
A n B n+1 C n-1 D n/2
3. 一个具有 767 个节点的完全二叉树,其叶子节点个数为( )
A 383 B 384 C 385 D 386
4. 一棵完全二叉树的节点数为 531 个,那么这棵树的高度为( )
A 11 B 10 C 8 D 12
📝 参考答案与解析
答案:1.B 2.A 3.B 4.B
第 1 题 :由 n0 = n2 + 1,n2 = 199,得 n0 = 200 → B
第 2 题:完全二叉树中度为 1 的结点最多只有 1 个(即 n1 = 0 或 n1 = 1)
结点总数 2n 是偶数,说明 n1 不能为 0(否则 n0 + n2 = 2n,而 n0 = n2 + 1 会让两边一奇一偶,矛盾),所以 n1 = 1
由 n0 + 1 + n2 = 2n 且 n0 = n2 + 1,得 2 * n0 = 2n,即 n0 = n → A
第 3 题 :结点总数 767 为奇数,说明 n1 = 0
由 n0 + n2 = 767 且 n0 = n2 + 1,得 2 * n0 = 768,n0 = 384 → B
第 4 题 : k = ⌈ log 2 ( n + 1 ) ⌉ = ⌈ log 2 532 ⌉ k = \lceil \log_2(n+1) \rceil = \lceil \log_2 532 \rceil k=⌈log2(n+1)⌉=⌈log2532⌉
因为 2 9 = 512 < 532 ≤ 1024 = 2 10 2^9 = 512 < 532 \le 1024 = 2^{10} 29=512<532≤1024=210,所以上取整后 k = 10 → B
2.5 二叉树的存储
二叉树的存储结构分为:顺序存储 和类似于链表的链式存储
1. 顺序存储(下节「优先级队列(堆)」详细介绍)
用数组按下标存储完全二叉树的结点,靠 2i+1 / 2i+2 / (i-1)/2 定位孩子和父结点。它只适合完全二叉树,普通二叉树用数组存会浪费大量空间
2. 链式存储
二叉树的链式存储是通过一个一个的结点引用起来的,常见的表示方式有二叉 和三叉表示方式:
java
// 孩子表示法
class Node {
int val; // 数据域
Node left; // 左孩子的引用,常常代表左孩子为根的整棵左子树
Node right; // 右孩子的引用,常常代表右孩子为根的整棵右子树
}
// 孩子双亲表示法
class Node {
int val; // 数据域
Node left; // 左孩子的引用,常常代表左孩子为根的整棵左子树
Node right; // 右孩子的引用,常常代表右孩子为根的整棵右子树
Node parent; // 当前节点的根节点
}
💡 说明
孩子双亲表示法 后续在平衡树位置介绍,本文采用孩子表示法来构建二叉树
3️⃣ 二叉树的基本操作
3.1 前置说明
在学习二叉树的基本操作前,需要先创建一棵二叉树,然后才能学习其相关的基本操作
由于大家对二叉树结构掌握还不够深入,为了降低学习成本,此处手动快速创建一棵简单的二叉树 ,快速进入二叉树操作学习;等二叉树结构了解得差不多时,再回过头来研究二叉树真正的创建方式
java
public class BinaryTree {
public static class BTNode {
BTNode left;
BTNode right;
int value;
BTNode(int value) {
this.value = value;
}
}
private BTNode root;
public void createBinaryTree() {
BTNode node1 = new BTNode(1);
BTNode node2 = new BTNode(2);
BTNode node3 = new BTNode(3);
BTNode node4 = new BTNode(4);
BTNode node5 = new BTNode(5);
BTNode node6 = new BTNode(6);
root = node1;
node1.left = node2;
node2.left = node3;
node1.right = node4;
node4.left = node5;
node5.right = node6;
}
}
创建出来的树长这样:
#mermaid-svg-oxDtclSgCfdX3YnZ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-oxDtclSgCfdX3YnZ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-oxDtclSgCfdX3YnZ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-oxDtclSgCfdX3YnZ .error-icon{fill:#552222;}#mermaid-svg-oxDtclSgCfdX3YnZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-oxDtclSgCfdX3YnZ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-oxDtclSgCfdX3YnZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-oxDtclSgCfdX3YnZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-oxDtclSgCfdX3YnZ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-oxDtclSgCfdX3YnZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-oxDtclSgCfdX3YnZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-oxDtclSgCfdX3YnZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-oxDtclSgCfdX3YnZ .marker.cross{stroke:#333333;}#mermaid-svg-oxDtclSgCfdX3YnZ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-oxDtclSgCfdX3YnZ p{margin:0;}#mermaid-svg-oxDtclSgCfdX3YnZ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-oxDtclSgCfdX3YnZ .cluster-label text{fill:#333;}#mermaid-svg-oxDtclSgCfdX3YnZ .cluster-label span{color:#333;}#mermaid-svg-oxDtclSgCfdX3YnZ .cluster-label span p{background-color:transparent;}#mermaid-svg-oxDtclSgCfdX3YnZ .label text,#mermaid-svg-oxDtclSgCfdX3YnZ span{fill:#333;color:#333;}#mermaid-svg-oxDtclSgCfdX3YnZ .node rect,#mermaid-svg-oxDtclSgCfdX3YnZ .node circle,#mermaid-svg-oxDtclSgCfdX3YnZ .node ellipse,#mermaid-svg-oxDtclSgCfdX3YnZ .node polygon,#mermaid-svg-oxDtclSgCfdX3YnZ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-oxDtclSgCfdX3YnZ .rough-node .label text,#mermaid-svg-oxDtclSgCfdX3YnZ .node .label text,#mermaid-svg-oxDtclSgCfdX3YnZ .image-shape .label,#mermaid-svg-oxDtclSgCfdX3YnZ .icon-shape .label{text-anchor:middle;}#mermaid-svg-oxDtclSgCfdX3YnZ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-oxDtclSgCfdX3YnZ .rough-node .label,#mermaid-svg-oxDtclSgCfdX3YnZ .node .label,#mermaid-svg-oxDtclSgCfdX3YnZ .image-shape .label,#mermaid-svg-oxDtclSgCfdX3YnZ .icon-shape .label{text-align:center;}#mermaid-svg-oxDtclSgCfdX3YnZ .node.clickable{cursor:pointer;}#mermaid-svg-oxDtclSgCfdX3YnZ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-oxDtclSgCfdX3YnZ .arrowheadPath{fill:#333333;}#mermaid-svg-oxDtclSgCfdX3YnZ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-oxDtclSgCfdX3YnZ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-oxDtclSgCfdX3YnZ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oxDtclSgCfdX3YnZ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-oxDtclSgCfdX3YnZ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oxDtclSgCfdX3YnZ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-oxDtclSgCfdX3YnZ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-oxDtclSgCfdX3YnZ .cluster text{fill:#333;}#mermaid-svg-oxDtclSgCfdX3YnZ .cluster span{color:#333;}#mermaid-svg-oxDtclSgCfdX3YnZ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-oxDtclSgCfdX3YnZ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-oxDtclSgCfdX3YnZ rect.text{fill:none;stroke-width:0;}#mermaid-svg-oxDtclSgCfdX3YnZ .icon-shape,#mermaid-svg-oxDtclSgCfdX3YnZ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oxDtclSgCfdX3YnZ .icon-shape p,#mermaid-svg-oxDtclSgCfdX3YnZ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-oxDtclSgCfdX3YnZ .icon-shape .label rect,#mermaid-svg-oxDtclSgCfdX3YnZ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oxDtclSgCfdX3YnZ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-oxDtclSgCfdX3YnZ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-oxDtclSgCfdX3YnZ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 1
2
4
3
5
6
⚠️ 注意
上述代码并不是创建二叉树的方式,真正创建二叉树的方式后续详解(见 4.6 二叉树的构建及遍历)
再看二叉树基本操作前,回顾一下二叉树的概念,二叉树是:
- 空树
- 非空:根节点 + 根节点的左子树 + 根节点的右子树
从概念中可以看出,二叉树定义是递归式的,因此后续基本操作中基本都是按照该概念实现的
3.2 二叉树的遍历
3.2.1 什么是遍历?
学习二叉树结构,最简单的方式就是遍历
所谓**遍历(Traversal)**是指沿着某条搜索路线,依次对树中每个结点均做一次且仅做一次访问
访问结点所做的操作依赖于具体的应用问题(比如:打印节点内容、节点内容加 1)。遍历是二叉树上最重要的操作之一,是二叉树上进行其它运算之基础
在遍历二叉树时,如果没有进行某种约定,每个人都按照自己的方式遍历,得出的结果就比较混乱;如果按照某种规则进行约定,则每个人对于同一棵树的遍历结果肯定是相同的
如果 N 代表根节点,L 代表根节点的左子树,R 代表根节点的右子树,则根据遍历根节点的先后次序有以下遍历方式:
| 简称 | 全称 | 顺序 |
|---|---|---|
| NLR | 前序遍历(Preorder Traversal,亦称先序遍历) | 访问根结点 → 根的左子树 → 根的右子树 |
| LNR | 中序遍历(Inorder Traversal) | 根的左子树 → 根节点 → 根的右子树 |
| LRN | 后序遍历(Postorder Traversal) | 根的左子树 → 根的右子树 → 根节点 |
java
// 前序遍历
void preOrder(Node root);
// 中序遍历
void inOrder(Node root);
// 后序遍历
void postOrder(Node root);
💡 小技巧
「前中后」指的是根结点在什么时候被访问:
- 根在最前 → 前序
- 根在中间 → 中序
- 根在最后 → 后序
左子树永远在右子树前面
3.2.2 前、中、后序遍历(递归实现)
前序遍历的递归过程如下(以 3.1 创建的二叉树为例):
前序遍历 (NLR):先访问根,再递归左子树,最后递归右子树
preOrder(1)
├─ 访问 1
├─ preOrder(1.left = 2)
│ ├─ 访问 2
│ ├─ preOrder(2.left = 3)
│ │ ├─ 访问 3
│ │ ├─ preOrder(null) → 返回
│ │ └─ preOrder(null) → 返回
│ └─ preOrder(2.right = null) → 返回
└─ preOrder(1.right = 4)
├─ 访问 4
├─ preOrder(4.left = 5)
│ ├─ 访问 5
│ ├─ preOrder(null) → 返回
│ └─ preOrder(5.right = 6)
│ ├─ 访问 6
│ ├─ preOrder(null) → 返回
│ └─ preOrder(null) → 返回
└─ preOrder(4.right = null) → 返回
代码实现:
java
// 前序遍历:根 -> 左 -> 右
void preOrder(BTNode root) {
if (root == null) {
return; // 递归结束条件:空结点
}
System.out.print(root.value + " ");
preOrder(root.left); // 递归左子树
preOrder(root.right); // 递归右子树
}
// 中序遍历:左 -> 根 -> 右
void inOrder(BTNode root) {
if (root == null) {
return;
}
inOrder(root.left); // 递归左子树
System.out.print(root.value + " ");
inOrder(root.right); // 递归右子树
}
// 后序遍历:左 -> 右 -> 根
void postOrder(BTNode root) {
if (root == null) {
return;
}
postOrder(root.left); // 递归左子树
postOrder(root.right); // 递归右子树
System.out.print(root.value + " ");
}
中序与后序图解类似,可自己动手绘制,三种遍历结果如下:
前序遍历结果:1 2 3 4 5 6
中序遍历结果:3 2 1 5 4 6
后序遍历结果:3 2 5 6 4 1
🔑 三种遍历的共同点
代码结构完全一样 ,区别只在于「访问根结点」这行代码放在递归左子树之前、之间、之后
时间复杂度 :每个结点访问一次,O(n);空间复杂度:递归栈深度即树高,最坏 O(n)
3.2.3 层序遍历
除了前序、中序、后序遍历外,还可以对二叉树进行层序遍历
设二叉树的根节点所在层数为 1,层序遍历就是从所在二叉树的根节点出发:
- 首先访问第一层的树根节点
- 然后从左到右访问第 2 层上的节点
- 接着是第三层的节点,以此类推
自上而下、自左至右逐层访问树的结点的过程就是层序遍历
#mermaid-svg-oxDtclSgCfdX3YnZ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-oxDtclSgCfdX3YnZ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-oxDtclSgCfdX3YnZ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-oxDtclSgCfdX3YnZ .error-icon{fill:#552222;}#mermaid-svg-oxDtclSgCfdX3YnZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-oxDtclSgCfdX3YnZ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-oxDtclSgCfdX3YnZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-oxDtclSgCfdX3YnZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-oxDtclSgCfdX3YnZ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-oxDtclSgCfdX3YnZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-oxDtclSgCfdX3YnZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-oxDtclSgCfdX3YnZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-oxDtclSgCfdX3YnZ .marker.cross{stroke:#333333;}#mermaid-svg-oxDtclSgCfdX3YnZ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-oxDtclSgCfdX3YnZ p{margin:0;}#mermaid-svg-oxDtclSgCfdX3YnZ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-oxDtclSgCfdX3YnZ .cluster-label text{fill:#333;}#mermaid-svg-oxDtclSgCfdX3YnZ .cluster-label span{color:#333;}#mermaid-svg-oxDtclSgCfdX3YnZ .cluster-label span p{background-color:transparent;}#mermaid-svg-oxDtclSgCfdX3YnZ .label text,#mermaid-svg-oxDtclSgCfdX3YnZ span{fill:#333;color:#333;}#mermaid-svg-oxDtclSgCfdX3YnZ .node rect,#mermaid-svg-oxDtclSgCfdX3YnZ .node circle,#mermaid-svg-oxDtclSgCfdX3YnZ .node ellipse,#mermaid-svg-oxDtclSgCfdX3YnZ .node polygon,#mermaid-svg-oxDtclSgCfdX3YnZ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-oxDtclSgCfdX3YnZ .rough-node .label text,#mermaid-svg-oxDtclSgCfdX3YnZ .node .label text,#mermaid-svg-oxDtclSgCfdX3YnZ .image-shape .label,#mermaid-svg-oxDtclSgCfdX3YnZ .icon-shape .label{text-anchor:middle;}#mermaid-svg-oxDtclSgCfdX3YnZ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-oxDtclSgCfdX3YnZ .rough-node .label,#mermaid-svg-oxDtclSgCfdX3YnZ .node .label,#mermaid-svg-oxDtclSgCfdX3YnZ .image-shape .label,#mermaid-svg-oxDtclSgCfdX3YnZ .icon-shape .label{text-align:center;}#mermaid-svg-oxDtclSgCfdX3YnZ .node.clickable{cursor:pointer;}#mermaid-svg-oxDtclSgCfdX3YnZ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-oxDtclSgCfdX3YnZ .arrowheadPath{fill:#333333;}#mermaid-svg-oxDtclSgCfdX3YnZ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-oxDtclSgCfdX3YnZ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-oxDtclSgCfdX3YnZ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oxDtclSgCfdX3YnZ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-oxDtclSgCfdX3YnZ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oxDtclSgCfdX3YnZ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-oxDtclSgCfdX3YnZ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-oxDtclSgCfdX3YnZ .cluster text{fill:#333;}#mermaid-svg-oxDtclSgCfdX3YnZ .cluster span{color:#333;}#mermaid-svg-oxDtclSgCfdX3YnZ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-oxDtclSgCfdX3YnZ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-oxDtclSgCfdX3YnZ rect.text{fill:none;stroke-width:0;}#mermaid-svg-oxDtclSgCfdX3YnZ .icon-shape,#mermaid-svg-oxDtclSgCfdX3YnZ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oxDtclSgCfdX3YnZ .icon-shape p,#mermaid-svg-oxDtclSgCfdX3YnZ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-oxDtclSgCfdX3YnZ .icon-shape .label rect,#mermaid-svg-oxDtclSgCfdX3YnZ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oxDtclSgCfdX3YnZ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-oxDtclSgCfdX3YnZ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-oxDtclSgCfdX3YnZ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 1
2
4
3
5
6
层序遍历结果:1 2 4 3 5 6
层序遍历需要借助队列来实现:
java
void levelOrder(BTNode root) {
if (root == null) {
return;
}
Queue<BTNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
BTNode cur = queue.poll();
System.out.print(cur.value + " ");
if (cur.left != null) {
queue.offer(cur.left);
}
if (cur.right != null) {
queue.offer(cur.right);
}
}
}
| 遍历方式 | 实现方式 | 顺序 |
|---|---|---|
| 前序遍历 | 递归 / 栈 | 根 → 左 → 右 |
| 中序遍历 | 递归 / 栈 | 左 → 根 → 右 |
| 后序遍历 | 递归 / 栈 | 左 → 右 → 根 |
| 层序遍历 | 队列 | 一层一层从左到右 |
3.2.4 遍历练习题
1. 某完全二叉树按层次输出(同一层从左到右)的序列为 ABCDEFGH,该完全二叉树的前序序列为( )
A: ABDHECFG B: ABCDEFGH C: HDBEAFCG D: HDEBFGCA
2. 二叉树的先序遍历和中序遍历如下:先序遍历:EFHIGJK;中序遍历:HFIEJKG,则二叉树根结点为( )
A: E B: F C: G D: H
3. 设一棵二叉树的中序遍历序列:badce,后序遍历序列:bdeca,则二叉树前序遍历序列为( )
A: adbce B: decab C: debac D: abcde
4. 某二叉树的后序遍历序列与中序遍历序列相同,均为 ABCDEF,则按层次输出(同一层从左到右)的序列为( )
A: FEDCBA B: CBAFED C: DEFCBA D: ABCDEF
📝 参考答案与解析
答案:1.A 2.A 3.D 4.A
第 1 题:层序 ABCDEFGH 对应的完全二叉树为:
A
/ \
B C
/ \ / \
D E F G
/
H
前序:A B D H E C F G → A
第 2 题 :前序遍历第一个 结点就是根结点,前序为 EFHIGJK,故根为 E → A
第 3 题 :后序遍历最后一个结点是根,所以根 = a
中序 badce 中 a 左边是 b、右边是 dce → 左子树只有 b,右子树是 dce
右子树后序 dec 中最后一个是 c,故右子树根为 c;中序 dce 中 c 左边 d、右边 e
还原出的树为:
a
/ \
b c
/ \
d e
前序:a b c d e → D
第 4 题:后序与中序相同,均为 ABCDEF。后序最后一个 F 是根,中序中 F 左边是 A~E,右边为空
递归下去可知这棵树是只有左孩子的一条链:F → E → D → C → B → A
层序输出即 F E D C B A → A
3.3 二叉树的其他基本操作
题目要求实现的接口如下:
java
// 获取树中节点的个数
int size(Node root);
// 获取叶子节点的个数
int getLeafNodeCount(Node root);
// 获取第 K 层节点的个数
int getKLevelNodeCount(Node root, int k);
// 获取二叉树的高度
int getHeight(Node root);
// 检测值为 value 的元素是否存在
Node find(Node root, int val);
// 层序遍历
void levelOrder(Node root);
// 判断一棵树是不是完全二叉树
boolean isCompleteTree(Node root);
完整实现(接 3.1 的 BinaryTree 类):
java
/**
* 获取树中节点的个数
* 子问题思路:整棵树的结点数 = 1(自己) + 左子树的结点数 + 右子树的结点数
*/
public int size(BTNode root) {
if (root == null) {
return 0;
}
return 1 + size(root.left) + size(root.right);
}
/**
* 获取叶子节点的个数
* 叶子结点:左孩子和右孩子都为 null
*/
public int getLeafNodeCount(BTNode root) {
if (root == null) {
return 0;
}
if (root.left == null && root.right == null) {
return 1; // 是叶子结点
}
return getLeafNodeCount(root.left) + getLeafNodeCount(root.right);
}
/**
* 获取第 K 层节点的个数
* 子问题思路:求 root 的第 K 层 ==> 求 root.left 的第 K-1 层 + root.right 的第 K-1 层
*/
public int getKLevelNodeCount(BTNode root, int k) {
if (root == null || k <= 0) {
return 0;
}
if (k == 1) {
return 1; // 第 1 层就是自己
}
return getKLevelNodeCount(root.left, k - 1)
+ getKLevelNodeCount(root.right, k - 1);
}
/**
* 获取二叉树的高度
* 整棵树的高度 = max(左子树高度, 右子树高度) + 1
*/
public int getHeight(BTNode root) {
if (root == null) {
return 0;
}
return Math.max(getHeight(root.left), getHeight(root.right)) + 1;
}
/**
* 检测值为 value 的元素是否存在,存在返回该结点,不存在返回 null
*/
public BTNode find(BTNode root, int val) {
if (root == null) {
return null;
}
if (root.value == val) {
return root; // 找到了
}
BTNode leftRet = find(root.left, val);
if (leftRet != null) { // 左子树中找到就不用找右子树了
return leftRet;
}
return find(root.right, val);
}
/**
* 判断一棵树是不是完全二叉树
* 思路:层序遍历,遇到第一个 null 之后,后面不能再出现非空结点
*/
public boolean isCompleteTree(BTNode root) {
if (root == null) {
return true; // 空树也是完全二叉树
}
Queue<BTNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
BTNode cur = queue.poll();
if (cur != null) {
queue.offer(cur.left); // 空结点也入队,用于占位判断
queue.offer(cur.right);
} else {
// 遇到第一个空结点后,队列中剩下的必须全是 null
while (!queue.isEmpty()) {
if (queue.poll() != null) {
return false;
}
}
return true;
}
}
return true;
}
💡 递归写法的通用套路
- 先写递归结束条件 (
root == null)- 再写当前结点要做的事
- 最后交给左右子树 (
+ size(root.left) + size(root.right))不要试图在脑子里展开整棵树的递归,只关注「当前这一层」和「下一层的关系」
4️⃣ 二叉树相关 OJ 面试题
把上面的操作练熟之后,就可以挑战下面这 14 道经典题目了。全部都可以用递归解决,建议先自己想,再看答案
| # | 题目 | 难度 | 链接 |
|---|---|---|---|
| 1 | 检查两棵树是否相同 | 简单 | LeetCode 100 |
| 2 | 另一棵树的子树 | 简单 | LeetCode 572 |
| 3 | 翻转二叉树 | 简单 | LeetCode 226 |
| 4 | 判断一棵二叉树是否是平衡二叉树 | 简单 | LeetCode 110 |
| 5 | 对称二叉树 | 简单 | LeetCode 101 |
| 6 | 二叉树的构建及遍历 | 简单 | 牛客 KY11 |
| 7 | 二叉树的分层遍历 | 中等 | LeetCode 102 |
| 8 | 最近公共祖先 | 中等 | LeetCode 236 |
| 9 | 前序与中序构造二叉树 | 中等 | LeetCode 105 |
| 10 | 中序与后序构造二叉树 | 中等 | LeetCode 106 |
| 11 | 二叉树创建字符串 | 简单 | LeetCode 606 |
| 12 | 前序非递归遍历 | 简单 | LeetCode 144 |
| 13 | 中序非递归遍历 | 简单 | LeetCode 94 |
| 14 | 后序非递归遍历 | 简单 | LeetCode 145 |
4.1 检查两棵树是否相同
题目链接 :100. 相同的树 - 力扣(LeetCode)
思路:
判断两棵树是否相同,就是判断根是否相同 且左子树是否相同 且右子树是否相同,天然递归
- 如果两棵树的根都为 null ,说明这两棵空树相同 →
true - 如果其中一个为 null、另一个不为 null ,结构不同 →
false - 如果根的值不同 →
false - 否则递归判断:
左子树相同 && 右子树相同
java
class Solution {
public boolean isSameTree(TreeNode p, TreeNode q) {
// 1. 都为空,相同
if (p == null && q == null) {
return true;
}
// 2. 一个为空、一个不为空,结构不同
if (p == null || q == null) {
return false;
}
// 3. 根的值不同
if (p.val != q.val) {
return false;
}
// 4. 递归判断左右子树
return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
}
}
复杂度:时间 O(min(n, m)),空间 O(min(h1, h2))(递归栈深度)
4.2 另一棵树的子树
题目链接 :572. 另一棵树的子树 - 力扣(LeetCode)
思路:
遍历 root 的每一个结点,判断「以该结点为根的子树」是否和 subRoot 相同;只要有一处相同就返回 true
- 先判断
root自己这棵树是否和subRoot相同 - 再递归去
root.left和root.right里找
java
class Solution {
public boolean isSubtree(TreeNode root, TreeNode subRoot) {
if (root == null) {
return false; // 遍历完了都没找到
}
// 当前结点为根的子树 与 subRoot 相同
if (isSameTree(root, subRoot)) {
return true;
}
// 去左右子树中继续找
return isSubtree(root.left, subRoot) || isSubtree(root.right, subRoot);
}
// 直接复用上面的「相同的树」方法
private boolean isSameTree(TreeNode p, TreeNode q) {
if (p == null && q == null) {
return true;
}
if (p == null || q == null) {
return false;
}
if (p.val != q.val) {
return false;
}
return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
}
}
复杂度:时间 O(n × m),空间 O(max(h1, h2))
4.3 翻转二叉树
题目链接 :226. 翻转二叉树 - 力扣(LeetCode)
思路:
翻转整棵树 = 交换根的左右孩子 + 翻转左子树 + 翻转右子树
交换左右孩子之后,原来的左子树变成了右子树,所以递归时对 root.left 和 root.right 调用即可
4 4
/ \ / \
2 7 ==> 7 2
/ \ / \ / \ / \
1 3 6 9 9 6 3 1
java
class Solution {
public TreeNode invertTree(TreeNode root) {
if (root == null) {
return null;
}
// 交换当前结点的左右孩子
TreeNode tmp = root.left;
root.left = root.right;
root.right = tmp;
// 递归翻转左右子树
invertTree(root.left);
invertTree(root.right);
return root;
}
}
复杂度:时间 O(n),空间 O(h)
4.4 判断一棵二叉树是否是平衡二叉树
题目链接 :110. 平衡二叉树 - 力扣(LeetCode)
思路:
平衡二叉树 :每个结点的左右子树高度差的绝对值不超过 1
朴素做法 :对每个结点都求一次左右子树高度,判断差值是否 <= 1------但这样会重复求高度,时间复杂度 O(n²)
优化做法(推荐) :在求高度的过程中顺便判断是否平衡
- 约定:返回
-1表示「以当前结点为根的子树已经不平衡」 - 返回
>= 0表示这棵子树的高度 - 只要某一层发现不平衡,就把
-1一路往上抛,提前结束
java
class Solution {
public boolean isBalanced(TreeNode root) {
return getHeight(root) != -1;
}
/**
* 返回值 >= 0:以 root 为根的子树的高度
* 返回值 = -1:以 root 为根的子树不是平衡二叉树
*/
private int getHeight(TreeNode root) {
if (root == null) {
return 0;
}
int leftH = getHeight(root.left);
if (leftH == -1) { // 左子树已经不平衡
return -1;
}
int rightH = getHeight(root.right);
if (rightH == -1) { // 右子树已经不平衡
return -1;
}
if (Math.abs(leftH - rightH) > 1) { // 当前结点不平衡
return -1;
}
return Math.max(leftH, rightH) + 1; // 返回高度
}
}
复杂度:时间 O(n)(每个结点只访问一次),空间 O(h)
4.5 对称二叉树
题目链接 :101. 对称二叉树 - 力扣(LeetCode)
思路:
判断整棵树是否轴对称,等价于判断左子树和右子树是否互为镜像
判断两棵树 p、q 是否互为镜像:
- 都为 null → 是镜像
- 只有一个为 null → 不是镜像
- 值不同 → 不是镜像
- 值相同 →
p.left与q.right互为镜像 且p.right与q.left互为镜像(注意是交叉比较)
java
class Solution {
public boolean isSymmetric(TreeNode root) {
if (root == null) {
return true;
}
return isMirror(root.left, root.right);
}
// 判断 p 和 q 是否互为镜像
private boolean isMirror(TreeNode p, TreeNode q) {
if (p == null && q == null) {
return true;
}
if (p == null || q == null) {
return false;
}
if (p.val != q.val) {
return false;
}
// 注意这里是「交叉」比较
return isMirror(p.left, q.right) && isMirror(p.right, q.left);
}
}
💡 对比「相同的树」
- 相同的树:
isSame(p.left, q.left) && isSame(p.right, q.right)------ 同侧比较- 对称二叉树:
isMirror(p.left, q.right) && isMirror(p.right, q.left)------ 交叉比较两个题只差这么一行,可以放在一起记忆
4.6 二叉树的构建及遍历
题目链接 :二叉树的构建及遍历 - 牛客网
题目 :编一个程序,读入用户输入的一串先序遍历 字符串,根据此字符串建立一个二叉树(# 表示空结点),然后中序遍历输出
例如输入 abc##de#g##f###,输出 c b e g d f a
思路:
这才是「真正的创建二叉树的方式」------按照前序遍历的顺序反序列化
- 定义全局下标
index,指向字符串当前要读的字符 - 读到一个字符,
index++ - 如果是
#,返回null(空结点) - 否则创建结点,然后先递归建左子树,再递归建右子树(顺序不能反,因为输入就是先序)
java
import java.util.Scanner;
public class Main {
static int index = 0; // 全局下标,记录读到哪一个字符
static class TreeNode {
char val;
TreeNode left;
TreeNode right;
TreeNode(char val) {
this.val = val;
}
}
// 按前序遍历的顺序读取字符串,构建二叉树
public static TreeNode createTree(String str) {
if (index >= str.length()) {
return null;
}
char c = str.charAt(index++);
if (c == '#') { // # 表示空结点
return null;
}
TreeNode root = new TreeNode(c);
root.left = createTree(str); // 先建左子树
root.right = createTree(str); // 再建右子树
return root;
}
// 中序遍历
public static void inOrder(TreeNode root) {
if (root == null) {
return;
}
inOrder(root.left);
System.out.print(root.val + " ");
inOrder(root.right);
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
while (in.hasNext()) {
index = 0; // 多组输入,记得重置下标
String str = in.next();
TreeNode root = createTree(str);
inOrder(root);
System.out.println();
}
}
}
⚠️ 易错点
index必须是全局(成员)变量 ,不能作为参数传递------因为递归过程中所有层都在共享同一个下标
4.7 二叉树的分层遍历
题目链接 :102. 二叉树的层序遍历 - 力扣(LeetCode)
思路:
在普通层序遍历的基础上,需要把每一层的结点分到同一个 List 里
关键技巧:在每轮循环开始前,先记录当前队列的大小 size ,这个 size 就是当前这一层的结点个数 ,然后只弹出 size 个结点
java
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> ret = new ArrayList<>();
if (root == null) {
return ret;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
int size = queue.size(); // 当前这一层的结点个数
List<Integer> row = new ArrayList<>();
while (size-- > 0) { // 只处理这一层
TreeNode cur = queue.poll();
row.add(cur.val);
if (cur.left != null) {
queue.offer(cur.left);
}
if (cur.right != null) {
queue.offer(cur.right);
}
}
ret.add(row); // 这一层收集完毕
}
return ret;
}
}
复杂度:时间 O(n),空间 O(n)(队列中最宽一层的大小)
4.8 二叉树的最近公共祖先
题目链接 :236. 二叉树的最近公共祖先 - 力扣(LeetCode)
思路:
对于结点 root,递归查找 p 和 q,会有以下几种情况:
root为 null,或者root就是 p 或 q → 直接返回root- 在左子树 中找到的返回值记为
left,在右子树 中找到的记为right - 如果
left和right都不为 null → 说明 p、q 分别在两侧 → 当前root就是最近公共祖先 - 如果只有一边不为 null → 说明 p、q 都在那一侧,返回不为 null 的那个
java
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
// 递归结束条件:走到空,或者找到了 p / q
if (root == null || root == p || root == q) {
return root;
}
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
if (left != null && right != null) {
return root; // p、q 分别在左右两侧
}
return left != null ? left : right; // 都在同一侧
}
}
复杂度:时间 O(n),空间 O(h)
4.9 根据前序与中序遍历构造二叉树
题目链接 :105. 从前序与中序遍历序列构造二叉树 - 力扣(LeetCode)
思路:
- 前序 的特点:第一个元素一定是根结点
- 中序 的特点:根结点左边 是左子树的全部结点,右边是右子树的全部结点
所以:
-
从前序中取出根结点(用
preIndex逐个往后取) -
在中序的
[inBegin, inEnd]区间里找到根结点的位置rootIndex -
左子树区间:
[inBegin, rootIndex - 1],右子树区间:[rootIndex + 1, inEnd] -
递归构造左右子树
前序:[3 | 9 | 20 15 7]
中序:[9 | 3 | 15 20 7]
↑ ↑ ↑
左子树 根 右子树
java
class Solution {
private int preIndex = 0; // 前序数组的下标
public TreeNode buildTree(int[] preorder, int[] inorder) {
return build(preorder, inorder, 0, inorder.length - 1);
}
private TreeNode build(int[] preorder, int[] inorder, int inBegin, int inEnd) {
if (inBegin > inEnd) { // 区间为空,没有结点了
return null;
}
// 1. 前序中取出根结点
TreeNode root = new TreeNode(preorder[preIndex++]);
// 2. 在中序区间中找到根结点的位置
int rootIndex = inBegin;
while (inorder[rootIndex] != root.val) {
rootIndex++;
}
// 3. 递归构造左右子树
root.left = build(preorder, inorder, inBegin, rootIndex - 1);
root.right = build(preorder, inorder, rootIndex + 1, inEnd);
return root;
}
}
复杂度 :时间 O(n²)(查找根结点位置用线性查找);若改用 HashMap 记录中序下标,可优化到 O(n)
4.10 根据中序与后序遍历构造二叉树
题目链接 :106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode)
思路:
和上一题几乎一样,区别在于:
- 后序 的特点:最后一个元素是根结点
- 所以
postIndex要从后往前取 - 递归时必须先建右子树、再建左子树(因为后序是「左 → 右 → 根」,从后往前取到的顺序是「根 → 右 → 左」)
java
class Solution {
private int postIndex;
public TreeNode buildTree(int[] inorder, int[] postorder) {
postIndex = postorder.length - 1; // 从后往前取
return build(inorder, postorder, 0, inorder.length - 1);
}
private TreeNode build(int[] inorder, int[] postorder, int inBegin, int inEnd) {
if (inBegin > inEnd) {
return null;
}
// 1. 后序中取出根结点(从后往前)
TreeNode root = new TreeNode(postorder[postIndex--]);
// 2. 在中序区间中找到根结点的位置
int rootIndex = inBegin;
while (inorder[rootIndex] != root.val) {
rootIndex++;
}
// 3. 先构造右子树,再构造左子树
root.right = build(inorder, postorder, rootIndex + 1, inEnd);
root.left = build(inorder, postorder, inBegin, rootIndex - 1);
return root;
}
}
💡 小结:三种「构造」组合
给出的序列 能否唯一确定一棵二叉树 前序 + 中序 ✅ 可以 中序 + 后序 ✅ 可以 前序 + 后序 ❌ 不可以(无法区分只有一个孩子时是左还是右)
4.11 二叉树创建字符串
题目链接 :606. 根据二叉树创建字符串 - 力扣(LeetCode)
思路:
前序遍历,按题目要求加括号,分三种情况:
- 左右孩子都为空 (叶子结点):直接返回值,不加任何括号
- 只有右孩子 :左孩子为空也必须输出一对空括号
(),否则会被解析成兄弟关系 - 有左孩子 :左孩子正常加括号;如果右孩子为空,右孩子的括号可以省略
java
class Solution {
public String tree2str(TreeNode t) {
if (t == null) {
return "";
}
// 情况 1:叶子结点,不需要括号
if (t.left == null && t.right == null) {
return t.val + "";
}
// 情况 2:右孩子为空,只需要左孩子的括号
if (t.right == null) {
return t.val + "(" + tree2str(t.left) + ")";
}
// 情况 3:左右孩子都不为空
return t.val + "(" + tree2str(t.left) + ")(" + tree2str(t.right) + ")";
}
}
复杂度:时间 O(n),空间 O(h)
4.12 二叉树前序非递归遍历实现
题目链接 :144. 二叉树的前序遍历 - 力扣(LeetCode)
思路:
借助栈 模拟递归。前序是「根 → 左 → 右」,而栈是后进先出,所以:
先压右孩子,再压左孩子,这样弹出时就能保证先访问左孩子
java
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> ret = new ArrayList<>();
if (root == null) {
return ret;
}
Deque<TreeNode> stack = new ArrayDeque<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode cur = stack.pop();
ret.add(cur.val); // 访问根(一弹出就访问)
if (cur.right != null) {
stack.push(cur.right); // 先压右
}
if (cur.left != null) {
stack.push(cur.left); // 再压左,保证左先出栈
}
}
return ret;
}
}
4.13 二叉树中序非递归遍历实现
题目链接 :94. 二叉树的中序遍历 - 力扣(LeetCode)
思路:
中序是「左 → 根 → 右」。核心逻辑:一路向左压栈,压不动了就弹出访问,然后转向右子树
cur = rootcur不为 null 时:把cur压栈,cur = cur.left(一路向左)cur为 null 时:弹出栈顶并访问 ,然后cur = 栈顶.right(转向右子树)- 循环结束条件:
cur == null且 栈为空
java
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> ret = new ArrayList<>();
Deque<TreeNode> stack = new ArrayDeque<>();
TreeNode cur = root;
while (cur != null || !stack.isEmpty()) {
while (cur != null) { // 一路向左,把沿途结点全部压栈
stack.push(cur);
cur = cur.left;
}
TreeNode top = stack.pop(); // 弹出栈顶并访问
ret.add(top.val);
cur = top.right; // 转向右子树
}
return ret;
}
}
4.14 二叉树后序非递归遍历实现
题目链接 :145. 二叉树的后序遍历 - 力扣(LeetCode)
思路(取巧但好写):
- 前序的规律是「根 → 左 → 右」,如果改成「根 → 右 → 左 」,最后再把结果反转,就得到了「左 → 右 → 根」------正是后序!
- 用
LinkedList的addFirst()头插,相当于边遍历边反转
java
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
LinkedList<Integer> ret = new LinkedList<>();
if (root == null) {
return ret;
}
Deque<TreeNode> stack = new ArrayDeque<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode cur = stack.pop();
ret.addFirst(cur.val); // 头插,相当于最后反转
if (cur.left != null) {
stack.push(cur.left); // 先压左,后弹出右
}
if (cur.right != null) {
stack.push(cur.right);
}
}
return ret;
}
}
💡 另一种标准写法
用
prev记录「上一次访问的结点」,判断栈顶的右子树是否已经访问过:
javaTreeNode cur = root, prev = null; while (cur != null || !stack.isEmpty()) { while (cur != null) { stack.push(cur); cur = cur.left; } cur = stack.peek(); if (cur.right == null || cur.right == prev) { // 右子树为空或已访问 ret.add(cur.val); stack.pop(); prev = cur; cur = null; } else { cur = cur.right; } }
📊 知识图谱总结
#mermaid-svg-rGWbf9WAFAePfKLW{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-rGWbf9WAFAePfKLW .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-rGWbf9WAFAePfKLW .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-rGWbf9WAFAePfKLW .error-icon{fill:#552222;}#mermaid-svg-rGWbf9WAFAePfKLW .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-rGWbf9WAFAePfKLW .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-rGWbf9WAFAePfKLW .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-rGWbf9WAFAePfKLW .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-rGWbf9WAFAePfKLW .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-rGWbf9WAFAePfKLW .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-rGWbf9WAFAePfKLW .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-rGWbf9WAFAePfKLW .marker{fill:#333333;stroke:#333333;}#mermaid-svg-rGWbf9WAFAePfKLW .marker.cross{stroke:#333333;}#mermaid-svg-rGWbf9WAFAePfKLW svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-rGWbf9WAFAePfKLW p{margin:0;}#mermaid-svg-rGWbf9WAFAePfKLW .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-rGWbf9WAFAePfKLW .cluster-label text{fill:#333;}#mermaid-svg-rGWbf9WAFAePfKLW .cluster-label span{color:#333;}#mermaid-svg-rGWbf9WAFAePfKLW .cluster-label span p{background-color:transparent;}#mermaid-svg-rGWbf9WAFAePfKLW .label text,#mermaid-svg-rGWbf9WAFAePfKLW span{fill:#333;color:#333;}#mermaid-svg-rGWbf9WAFAePfKLW .node rect,#mermaid-svg-rGWbf9WAFAePfKLW .node circle,#mermaid-svg-rGWbf9WAFAePfKLW .node ellipse,#mermaid-svg-rGWbf9WAFAePfKLW .node polygon,#mermaid-svg-rGWbf9WAFAePfKLW .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-rGWbf9WAFAePfKLW .rough-node .label text,#mermaid-svg-rGWbf9WAFAePfKLW .node .label text,#mermaid-svg-rGWbf9WAFAePfKLW .image-shape .label,#mermaid-svg-rGWbf9WAFAePfKLW .icon-shape .label{text-anchor:middle;}#mermaid-svg-rGWbf9WAFAePfKLW .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-rGWbf9WAFAePfKLW .rough-node .label,#mermaid-svg-rGWbf9WAFAePfKLW .node .label,#mermaid-svg-rGWbf9WAFAePfKLW .image-shape .label,#mermaid-svg-rGWbf9WAFAePfKLW .icon-shape .label{text-align:center;}#mermaid-svg-rGWbf9WAFAePfKLW .node.clickable{cursor:pointer;}#mermaid-svg-rGWbf9WAFAePfKLW .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-rGWbf9WAFAePfKLW .arrowheadPath{fill:#333333;}#mermaid-svg-rGWbf9WAFAePfKLW .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-rGWbf9WAFAePfKLW .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-rGWbf9WAFAePfKLW .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-rGWbf9WAFAePfKLW .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-rGWbf9WAFAePfKLW .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-rGWbf9WAFAePfKLW .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-rGWbf9WAFAePfKLW .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-rGWbf9WAFAePfKLW .cluster text{fill:#333;}#mermaid-svg-rGWbf9WAFAePfKLW .cluster span{color:#333;}#mermaid-svg-rGWbf9WAFAePfKLW div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-rGWbf9WAFAePfKLW .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-rGWbf9WAFAePfKLW rect.text{fill:none;stroke-width:0;}#mermaid-svg-rGWbf9WAFAePfKLW .icon-shape,#mermaid-svg-rGWbf9WAFAePfKLW .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-rGWbf9WAFAePfKLW .icon-shape p,#mermaid-svg-rGWbf9WAFAePfKLW .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-rGWbf9WAFAePfKLW .icon-shape .label rect,#mermaid-svg-rGWbf9WAFAePfKLW .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-rGWbf9WAFAePfKLW .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-rGWbf9WAFAePfKLW .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-rGWbf9WAFAePfKLW :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 树 Tree
二叉树
每个结点度 <= 2
多叉树
孩子兄弟表示法
特殊形态
存储方式
遍历方式
常见 OJ 题
满二叉树
2^K - 1 个结点
完全二叉树
顺序编号 2i+1 / 2i+2
链式存储
left / right
顺序存储
数组 + 2i+1 公式
前序 NLR
中序 LNR
后序 LRN
层序
队列实现
相同的树 / 子树
翻转 / 对称
平衡二叉树
最近公共祖先
前中序 / 中后序 构造
📊 小结
| 知识点 | 核心内容 |
|---|---|
| 树的概念 | 递归定义、根/叶/度/层次、N 个结点有 N-1 条边 |
| 树的表示 | 孩子兄弟表示法(firstChild + nextBrother),树可转二叉树 |
| 二叉树概念 | 度 <= 2、左右有序、递归定义 |
| 满二叉树 | 每层都满,结点总数 2^K - 1 |
| 完全二叉树 | 逐层从左到右排满,满二叉树是其特例 |
| 重要性质 | 第 i 层最多 2^(i-1) 个;深度 K 最多 2^K - 1 个;n0 = n2 + 1 |
| 顺序存储 | 父 (i-1)/2,左 2i+1,右 2i+2(堆的基础) |
| 链式存储 | left / right 两个引用 |
| 前中后序遍历 | 递归三行代码,区别只在「访问根」的位置 |
| 层序遍历 | 借助队列,用 size 控制逐层输出 |
| 求结点个数 | 1 + size(left) + size(right) |
| 求高度 | max(height(left), height(right)) + 1 |
| 判断完全二叉树 | 层序遍历,遇到 null 后不能再出现非 null |
| 平衡二叉树 | 求高度时顺便判断,不平衡返回 -1 提前终止 |
| 构造二叉树 | 前序/后序定根,中序分左右;前序 + 后序不能唯一确定 |
| 非递归遍历 | 前序:先压右再压左;中序:一路向左;后序:根右左 + 反转 |
✍️ 写在最后
二叉树是数据结构当中挺重要的一种结构:它上承链表 的引用思想,下启堆、Map/Set、平衡树的实现基础
而二叉树最有价值的地方,是它把递归 这个思想体现得淋漓尽致------写完 size()、getHeight()、isBalanced() 这几道题,你对递归的理解会上一个台阶
学习建议:
- 先把三种遍历的递归写法默写出来
- 再把 14 道 OJ 题亲手敲一遍,不要只看答案
- 每道题都问自己:「当前这一层做什么,剩下的交给左右子树」
感谢DeepSeek帮忙对语言风格做了优化和将图片替换成Mermaid格式
如有问题或建议,欢迎在评论区留言交流! 🙌