C++详解二叉树遍历:递归与迭代实现及层序遍历应用

二叉树遍历C++实现递归算法
于 2026-07-31 06:59:41 修改
·本内容遵循CC 4.0 BY-SA版权协议

1. 项目概述:为什么树的遍历是算法基石

如果你刚开始接触数据结构与算法,或者正在准备技术面试,那么“树的遍历”这个概念,你大概率已经听过无数遍了。它就像学英语时的“主谓宾”结构,看似基础,却构成了理解更复杂算法(比如二叉搜索树的增删查改、平衡树的旋转、图论中的深度优先搜索)的绝对核心。我见过太多朋友,在刷LeetCode时,一遇到二叉树相关的题目就发怵,代码写出来总是差一点,根源往往就在于对几种遍历方式的理解只停留在“背模板”的层面,没有真正吃透它们的思想和变体。

所谓树的遍历,就是按照某种规则,不重复地访问树中的所有节点。这个“规则”的不同,就衍生出了我们常说的四种经典方式:前序遍历、中序遍历、后序遍历和层序遍历。别小看这四种遍历,它们不仅仅是输出节点值的顺序不同,其背后的访问逻辑和应用场景天差地别。前序遍历常用于复制一棵树或序列化;中序遍历是二叉搜索树输出有序序列的不二法门;后序遍历在计算子树属性(如高度、节点数)或进行析构时非常高效;而层序遍历则是解决与“深度”、“宽度”相关问题的最直观工具。

今天,我们就用C++这把“手术刀”,把这四种遍历方式从里到外彻底解剖一遍。我会带你从最基础的递归实现开始,这是理解遍历思想最直观的路径;然后,我们会一步步过渡到迭代实现,因为递归虽然简洁,但在面试或处理深度很大的树时,理解其等价的迭代栈操作是必备技能;最后,我们会深入到层序遍历,并探讨其在解决实际问题时的威力。无论你是刚入门的新手,还是想巩固基础的进阶者,这篇文章都会让你对树的遍历有一个全新的、透彻的认识。

2. 核心思路与递归实现:理解遍历的本质

在开始写代码之前,我们必须先统一思想:什么是“访问”?在树的语境下,访问一个节点,通常意味着读取或处理它的值。而遍历的核心矛盾在于,树是非线性的,一个节点可能有左、右两个孩子,我先访问谁?处理完当前节点后,下一步该去哪里?

递归之所以是讲解遍历的首选,是因为它完美契合了树的定义——一棵树由根节点和若干子树构成。处理整棵树,等价于处理根节点,再递归地处理左子树和右子树。区别仅在于这三者之间的执行顺序。

2.1 二叉树节点的C++定义

任何操作都需要先有数据结构。我们定义一个最标准的二叉树节点:

CPP
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

这个结构体包含一个整型值val,以及指向左、右子节点的指针。构造函数将节点值初始化为x,并将左右指针置为空(nullptr)。这是所有后续操作的基础。

注意:在实际项目中,你可能需要模板化val的类型以支持泛型,或者添加父节点指针以支持更多操作。但为了聚焦于遍历算法本身,我们使用这个最简单的版本。

2.2 前序遍历:根 -> 左 -> 右

前序遍历的访问顺序是:先访问根节点,然后递归地前序遍历左子树,最后递归地前序遍历右子树。这个顺序可以记为“根左右”。

递归函数实现:

CPP
void preorderTraversal(TreeNode* root, vector<int>& result) {
if (root == nullptr) {
return; // 递归基:如果节点为空,直接返回
}
result.push_back(root->val); // 访问根节点
preorderTraversal(root->left, result); // 遍历左子树
preorderTraversal(root->right, result); // 遍历右子树
}

为什么这样写? 递归的核心是“相信递归函数能完成它的工作”。我们假设preorderTraversal这个函数已经能正确地对一棵树进行前序遍历并结果存入result。那么,对于以root为根的树,我们要做的就是按顺序执行三件事:处理根、让函数去处理左子树、让函数去处理右子树。if (root == nullptr)这个判断至关重要,它是递归的终止条件,确保了当遇到空子树时,递归能够正确返回,不会产生无限递归或访问空指针。

一个具体的例子: 对于二叉树 [1,2,3,null,null,4,5](层序遍历表示),其前序遍历结果是 [1,2,3,4,5]。你可以手动模拟一下:从根1开始,访问1;走到左子树根2,访问2(2的左右子节点为空,递归返回);回到根1,走到右子树根3,访问3;走到3的左子树根4,访问4;回到3,走到3的右子树根5,访问5。

2.3 中序遍历:左 -> 根 -> 右

中序遍历的访问顺序是:先递归地中序遍历左子树,然后访问根节点,最后递归地中序遍历右子树。顺序是“左根右”。

递归函数实现:

CPP
void inorderTraversal(TreeNode* root, vector<int>& result) {
if (root == nullptr) {
return;
}
inorderTraversal(root->left, result); // 遍历左子树
result.push_back(root->val); // 访问根节点
inorderTraversal(root->right, result); // 遍历右子树
}

核心应用:二叉搜索树(BST)的性质。 这是中序遍历最重要的特性。对于一棵二叉搜索树(左子树所有节点值 < 根节点值 < 右子树所有节点值),中序遍历的结果必然是一个升序序列。这个性质被广泛应用于BST的验证、查找第K小的元素等问题。代码和递归思想完全没变,只是访问根节点的时机挪到了中间。

2.4 后序遍历:左 -> 右 -> 根

后序遍历的访问顺序是:先递归地后序遍历左子树,然后递归地后序遍历右子树,最后访问根节点。顺序是“左右根”。

递归函数实现:

CPP
void postorderTraversal(TreeNode* root, vector<int>& result) {
if (root == nullptr) {
return;
}
postorderTraversal(root->left, result); // 遍历左子树
postorderTraversal(root->right, result); // 遍历右子树
result.push_back(root->val); // 访问根节点
}

为什么后序遍历有用? 因为这种顺序意味着,当你访问一个节点时,它的所有子孙节点都已经被访问过了。这使得后序遍历非常适合处理一些“依赖子节点结果才能计算父节点”的场景。比如,计算二叉树中每个节点的高度(高度取决于左右子树的高度),或者安全地删除一棵树(必须先删除子节点,再删除父节点,否则会出现悬空指针)。在后续我们讨论迭代实现时,后序遍历也是最复杂的一种,因为它需要额外的状态来记录节点是否被访问过。

3. 迭代实现:用栈模拟递归过程

递归虽然清晰,但它隐式地使用了系统调用栈。在树深度很大时,有栈溢出的风险。并且,理解迭代实现能让你更深刻地把握遍历过程的本质。迭代法的核心就是:用我们自己维护的显式栈,来模拟系统栈的函数调用过程

3.1 前序遍历的迭代法

前序遍历的迭代思路是最直接的,因为它的访问顺序和我们手动模拟的思路一致:遇到一个节点,就访问它,然后优先处理它的左子树,右子树暂时压入栈中“备忘”。

算法步骤:

  1. 初始化一个空栈 stk,将根节点 root 压入栈。
  2. 当栈不为空时循环: a. 弹出栈顶节点 node。 b. 访问 node(将其值加入结果)。 c. 先将 node 的右子节点压栈(如果存在)。 d. 再将 node 的左子节点压栈(如果存在)
  3. 循环结束,返回结果。

C++实现:

CPP
vector<int> preorderTraversalIterative(TreeNode* root) {
vector<int> result;
if (root == nullptr) return result;
 
stack<TreeNode*> stk;
stk.push(root);
 
while (!stk.empty()) {
TreeNode* node = stk.top();
stk.pop();
result.push_back(node->val); // 访问根节点
 
// 注意入栈顺序:先右后左,保证出栈时是左先右后
if (node->right) stk.push(node->right);
if (node->left) stk.push(node->left);
}
return result;
}

为什么先右后左? 栈是“后进先出”的结构。我们希望下一个被处理(弹出)的节点是左子节点。因此,需要先把右子节点压进去,再把左子节点压进去。这样,左子节点就在栈顶,会先被弹出访问。这个过程完美模拟了递归中“先处理根,然后深入左子树,左子树全部处理完后再回溯处理右子树”的流程。

3.2 中序遍历的迭代法

中序遍历的迭代法比前序要稍复杂一些,因为访问节点的时机不是在入栈或出栈时,而是在“从栈中弹出,但准备转向右子树之前”。我们需要一个指针 curr 来帮助遍历。

算法步骤(“左链入栈”法):

  1. 初始化空栈 stk,指针 curr = root
  2. curr 不为空 栈不为空时循环(这个“或”很关键): a. 一路向左:如果 curr 不为空,将其压入栈,并令 curr = curr->left。这一步模拟递归深入左子树的过程。 b. 访问与转折:如果 curr 为空(意味着已经走到某条左链的尽头),则: i. 弹出栈顶节点 node,它就是当前应该访问的“根”节点。 ii. 访问 node。 iii. 令 curr = node->right,开始处理右子树。
  3. 循环结束,返回结果。

C++实现:

CPP
vector<int> inorderTraversalIterative(TreeNode* root) {
vector<int> result;
stack<TreeNode*> stk;
TreeNode* curr = root;
 
while (curr != nullptr || !stk.empty()) {
// 深入左子树
while (curr != nullptr) {
stk.push(curr);
curr = curr->left;
}
// 到达左子树尽头,回溯
curr = stk.top();
stk.pop();
result.push_back(curr->val); // 访问节点
// 转向右子树
curr = curr->right;
}
return result;
}

理解这个“双循环”结构: 外层循环的条件 curr != nullptr || !stk.empty() 保证了只要还有节点待处理,循环就不会停止。内层的 while 循环负责疯狂向左走,把路径上的节点都压入栈,这是模拟递归调用栈增长的过程。当走到头(curr为空)时,从栈中弹出一个节点访问,然后把这个节点的右子树当作一棵新的树,重复整个过程。这个算法非常优美地模拟了中序遍历的递归过程。

3.3 后序遍历的迭代法

后序遍历是迭代法中最有挑战性的一种,因为一个节点需要在它的左右子树都被访问后才能被访问。我们无法像前序或中序那样,在某个固定时机(刚遇到或从左子树返回时)就访问它。这里介绍一种利用前序变形和反转的取巧方法,以及一种更通用的标记法

方法一:前序变体 + 反转 我们观察前序遍历是“根左右”,后序遍历是“左右根”。如果我们能实现一种“根右左”的遍历,再把结果反转一下,不就得到“左右根”了吗?而“根右左”的迭代实现,只需把前序遍历迭代法中左右子节点的入栈顺序对调即可。

C++实现:

CPP
vector<int> postorderTraversalIterativeReverse(TreeNode* root) {
vector<int> result;
if (root == nullptr) return result;
stack<TreeNode*> stk;
stk.push(root);
 
while (!stk.empty()) {
TreeNode* node = stk.top();
stk.pop();
result.push_back(node->val); // 访问“根”
 
// 注意:这里是先左后右,以实现“根右左”的顺序
if (node->left) stk.push(node->left);
if (node->right) stk.push(node->right);
}
// 将“根右左”的结果反转,得到“左右根”
reverse(result.begin(), result.end());
return result;
}

这种方法代码非常简洁,容易记忆。但它需要一次额外的结果反转操作,并且遍历顺序本身并不是真正的后序,在某些需要边遍历边处理的场景下可能不适用。

方法二:标记法(推荐掌握) 这是更贴近后序遍历本质的迭代方法。我们给栈中的每个元素附加一个状态,标记这个节点是否已经被访问过(即其左右子树是否已处理)。

算法思路: 栈里不直接存 TreeNode*,而是存一个 pair<TreeNode*, bool>,其中 bool 表示该节点是否已被访问。初始时,节点状态为 false(未访问)。当从栈中弹出一个节点时:

  • 如果状态为 false,说明它的左右子树还没处理。那么我们将它重新压栈,但状态改为 true。接着,按照先右后左的顺序(因为栈是LIFO,我们希望左子树先被处理),将它的右、左子节点(如果存在)以 false 状态压栈。
  • 如果状态为 true,说明它的左右子树都已处理完毕,现在可以访问这个节点了。

C++实现:

CPP
vector<int> postorderTraversalIterative(TreeNode* root) {
vector<int> result;
if (root == nullptr) return result;
 
stack<pair<TreeNode*, bool>> stk;
stk.push({root, false});
 
while (!stk.empty()) {
auto [node, visited] = stk.top();
stk.pop();
 
if (visited) {
// 左右子树已处理,访问本节点
result.push_back(node->val);
} else {
// 本节点未访问,将其标记为已访问并重新入栈
stk.push({node, true});
// 先右后左入栈,保证左子树先被处理
if (node->right) stk.push({node->right, false});
if (node->left) stk.push({node->left, false});
}
}
return result;
}

这种方法虽然代码稍长,但它清晰地模拟了后序遍历的递归调用栈,通用性更强,且不需要反转结果。理解这种方法,你对栈在遍历中的作用会有质的飞跃。

4. 层序遍历:队列与广度优先搜索

层序遍历,顾名思义,就是按树的层级,从上到下、从左到右依次访问节点。它不属于深度优先遍历的范畴,而是一种广度优先搜索。实现它需要用到另一种数据结构:队列

4.1 基础层序遍历

队列的“先进先出”特性完美契合层序遍历的需求:我们将一层的节点按顺序放入队列,然后依次取出访问,同时在取出每个节点时,将其子节点放入队列尾部,这样就自然保证了下一层的节点会在当前层所有节点处理完后才被处理。

算法步骤:

  1. 初始化一个空队列 que,将根节点 root 入队。
  2. 当队列不为空时循环: a. 获取当前队列的大小 levelSize(这代表当前层的节点数)。 b. 循环 levelSize 次: i. 出队队首节点 node。 ii. 访问 node。 iii. 如果 node 有左子节点,将其入队。 iv. 如果 node 有右子节点,将其入队。 c. 当前层所有节点处理完毕,结果中已保存了这一层的节点值(或者你可以开始处理下一层)。
  3. 循环结束,返回结果。

C++实现:

CPP
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> result; // 结果是一个二维数组,每一层一个子数组
if (root == nullptr) return result;
 
queue<TreeNode*> que;
que.push(root);
 
while (!que.empty()) {
int levelSize = que.size(); // 关键:记录当前层的节点数
vector<int> currentLevel;
for (int i = 0; i < levelSize; ++i) {
TreeNode* node = que.front();
que.pop();
currentLevel.push_back(node->val); // 访问节点
 
// 将下一层的节点入队
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
result.push_back(currentLevel); // 将当前层结果加入总结果
}
return result;
}

关键点解析: 代码中 int levelSize = que.size(); 这一行至关重要。在开始处理一层之前,我们先记录下队列的长度,这个长度就是当前层所有节点的数量。然后我们只循环这么多次,这样就严格区分了当前层和下一层。如果不这样做,随着我们不断出队、入队,队列中会混合不同层的节点,就无法区分层级了。这种写法是解决所有二叉树层序遍历相关问题的标准模板。

4.2 层序遍历的变体与应用

掌握了基础的层序遍历模板,很多LeetCode上的中等难度题目就迎刃而解了。它们本质上都是在这个模板上稍作修改。

变体1:锯齿形(之字形)层序遍历 即先从左到右,下一层从右到左,如此交替。我们只需要在基础模板上,增加一个标志位 leftToRight 来记录当前层的遍历方向,然后在存储当前层结果时,根据方向决定是正向插入还是反向插入。

CPP
vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
vector<vector<int>> result;
if (!root) return result;
queue<TreeNode*> q;
q.push(root);
bool leftToRight = true;
 
while (!q.empty()) {
int size = q.size();
vector<int> level(size); // 预先分配空间
for (int i = 0; i < size; ++i) {
TreeNode* node = q.front(); q.pop();
// 根据方向决定插入位置
int index = leftToRight ? i : (size - 1 - i);
level[index] = node->val;
 
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
}
result.push_back(level);
leftToRight = !leftToRight; // 切换方向
}
return result;
}

变体2:寻找每层的最大值/平均值 这更简单了,在遍历每一层时,累加值或比较最大值即可,模板完全不变。

变体3:二叉树的最小深度 最小深度是指从根节点到最近叶子节点的路径上的节点数。用层序遍历做是最高效的,因为一旦我们遇到第一个叶子节点(左右子节点都为空),就可以立即返回当前深度,这就是最小深度。

CPP
int minDepth(TreeNode* root) {
if (root == nullptr) return 0;
queue<TreeNode*> q;
q.push(root);
int depth = 1; // 根节点自身算一层
 
while (!q.empty()) {
int size = q.size();
for (int i = 0; i < size; ++i) {
TreeNode* node = q.front(); q.pop();
// 找到第一个叶子节点,立即返回当前深度
if (node->left == nullptr && node->right == nullptr) {
return depth;
}
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
}
depth++; // 一层遍历完,深度+1
}
return depth; // 理论上不会走到这里
}

层序遍历在这里的优势是“广度优先”,它保证我们找到的第一个叶子节点,其深度一定是最小的。如果用深度优先搜索(递归),则需要遍历所有路径才能确定最小值。

5. 综合对比与实战心得

到现在为止,我们已经把四种遍历方式的递归和迭代实现都过了一遍。我们来做一个系统的对比和总结,并分享一些在实战和面试中的心得体会。

5.1 四种遍历方式对比总结

遍历方式 访问顺序 (递归定义) 核心思想与应用场景 迭代实现关键数据结构 迭代实现难点
前序遍历 根 -> 左 -> 右 “先处理当前,再处理子问题”。用于复制树、序列化、计算目录结构等。 相对简单,注意入栈顺序(先右后左)。
中序遍历 左 -> 根 -> 右 “先处理左子问题,再处理当前,最后处理右子问题”。BST相关问题的核心(得到有序序列)。 需要指针curr配合,理解“一路向左,回溯访问,转向右子树”的循环。
后序遍历 左 -> 右 -> 根 “先处理所有子问题,再处理当前”。适用于计算子树属性(高度、节点数)、析构树、某些路径问题。 最复杂。需用“标记法”或“前序变体+反转”来模拟访问时机。
层序遍历 按层,从左到右 广度优先搜索。用于求深度、宽度、层最大值、最短路径等与“层”相关的问题。 队列 需在每层循环前记录队列大小,以区分层级。

5.2 递归 vs. 迭代:如何选择?

  • 递归:代码简洁,逻辑与树定义高度一致,易于理解和编写。是表达树算法最自然的方式。缺点是存在函数调用开销,深度过大时可能导致栈溢出。在通常的算法题和面试中,如果树深度不大(比如平衡树),递归是首选。
  • 迭代:显式使用栈或队列,避免了递归的系统开销,可控性更强。缺点是代码相对复杂,尤其是中序和后序遍历。当题目明确要求不能使用递归,或者树可能非常深(如链状树)时,必须使用迭代法。

我的建议是: 初学者务必先掌握递归写法,深刻理解其分治思想。然后,必须掌握前序、中序、层序遍历的迭代写法,因为它们在面试中极为常见。后序遍历的迭代法(标记法)可以作为加分项去理解。

5.3 常见陷阱与调试技巧

  1. 空指针判断:这是最常见的运行时错误。在访问 node->leftnode->right 之前,一定要先判断 node 是否为空。递归的基准条件 if (root == nullptr) return; 和迭代中入队/入栈前的判断 if (node->left) ... 都是为了这个。
  2. 迭代中栈或队列的状态管理
    • 前序迭代:忘记“先右后左”的入栈顺序,会导致遍历顺序错误。
    • 中序迭代:混淆 curr 指针和栈顶元素的关系。记住 curr 代表当前要处理的“子树”,可能是空(意味着该回溯了)。
    • 后序标记法:忘记将节点重新压栈并标记为已访问,会导致节点丢失。
    • 层序遍历:最经典的错误是在循环内部使用 for (int i = 0; i < q.size(); ++i),因为 q.size() 在循环中是变化的!必须提前用变量 levelSize 固定住。
  3. 理解遍历序列的唯一性:仅凭一种遍历序列(如前序)通常无法唯一确定一棵二叉树的结构。但前序+中序后序+中序可以唯一确定。这是因为中序提供了左右子树的划分信息。这是一个常见的面试考点。
  4. 调试小技巧:对于复杂的迭代代码,可以用一个小二叉树(例如 [1,2,3])在纸上手动模拟栈/队列的变化,一步步跟踪变量的值,这是理解算法最有效的方法。

5.4 从遍历到解题:思路的延伸

树的遍历不仅仅是输出节点值。它更是一种访问所有节点并执行某种操作的框架。很多二叉树问题都可以被归结为“在某种遍历顺序下,计算或传递某种信息”。

  • 自顶向下:类似于前序遍历。在访问节点时,你将一些信息通过函数参数“带下去”给子节点。例如,“计算从根到每个叶子节点的路径和”。
  • 自底向上:类似于后序遍历。你先递归地解决子问题(左右子树),然后将子问题的结果组合起来解决当前节点的问题。例如,“判断一棵树是否是平衡二叉树”(需要子树的高度信息)。
  • 层序相关:任何需要按层处理,或求最短路径(最小深度)的问题,层序遍历都是第一选择。

当你拿到一个二叉树问题时,先问自己:解决这个问题,需要在访问节点时知道什么信息?这些信息是从父节点传来更方便(前序),还是从子节点汇总而来更方便(后序)?抑或是需要按层处理(层序)?想清楚了这一点,代码框架就呼之欲出了。

树的遍历是数据结构中最优美、最基础的部分之一。它连接了递归、栈、队列这些核心概念,也是解决无数复杂算法的起点。希望这篇近万字的详解,能帮你把这块基石打牢。剩下的,就是在具体的题目中反复练习和体会了。当你再看到二叉树问题时,能下意识地想到该用哪种遍历框架,那你的修炼就真正入门了。

二叉树理论基础 递归遍历 迭代遍历 统一迭代 层序遍历
本文全面介绍二叉树相关知识,包括种类(满二叉树、完全二叉树等)、存储方式(链式和顺序)、遍历方式(深度优先和广度优先)及定义。详细讲解递归迭代遍历的思路,如递归三要素,迭代实现前后中序遍历,还介绍统一迭代写法和层序遍历实现方法。
四代目 水门
1124
二叉树层序遍历(迭代法和递归法)
本文聚焦二叉树层序遍历,介绍两种实现方法。迭代法借助队列先进先出特性,按层处理节点;递归法通过递归调用函数,按层次结构存储节点值。文中详细阐述两种方法的思路、步骤,并给出解题代码,助读者掌握二叉树层序遍历
呜哈哈3424
1921
C++实现二叉树创建和遍历,超入门邻家小女也懂了
本文详细介绍了二叉树的定义、特点、性质以及创建方法,包括声明、成员运算符、批量创建等。同时,讨论了完全二叉树的创建,并展示了层序遍历、先序遍历、中序遍历、后序遍历递归和非递归实现。此外,还解释了二叉树遍历概念和相关术语。
Hann Yang
10014
二叉树的中序遍历
本文详细介绍了二叉树的四种遍历方式——前序、中序、后序和层序遍历。通过递归迭代两种方法解析解题思路,以中序遍历为例,展示了递归解法的递归公式和终止条件,以及迭代解法的具体步骤。此外,还提供了对应的C++代码实现
行路南
58544
代码随想录刷题记录(11)| 二叉树二叉树理论基础,二叉树递归遍历迭代遍历,统一迭代层序遍历
本文详细介绍了二叉树的基本概念、存储方式、遍历方式,并深入探讨了递归与迭代遍历的具体实现,包括前序、中序、后序遍历层序遍历等多种算法,同时提供了丰富的示例代码。
格子瑞风
1998
第十三天|二叉树遍历| 理论基础,递归遍历迭代遍历层序遍历
本文深入解析二叉树遍历方法,包括前序、中序、后序及层序遍历,探讨递归与迭代实现,讲解标记法统一迭代遍历,并提供多个LeetCode题目实例。
皎味小行家
1807
C++实现二叉树的各种遍历,包含递归迭代方法
本文详细介绍了二叉树的前序、中序、后序和层序遍历递归与迭代实现方法,包括C++代码示例,适合算法初学者和力扣刷题者参考。
学习侠
1288
代码随想录算法day11 | 二叉树part01 | 递归遍历迭代遍历,统一迭代层序遍历
本文深入讲解二叉树的各种遍历方式,包括前序、中序、后序遍历递归与迭代实现,以及统一迭代法,并探讨二叉树层序遍历
不是很强 但是很秃
1380
DAY12-13|二叉树Part01|二叉树递归遍历二叉树迭代遍历二叉树的统一迭代法、二叉树层序遍历
本文围绕二叉树遍历展开,介绍了递归遍历迭代遍历、统一迭代法和层序遍历递归遍历分三步,迭代遍历利用栈存储结点,统一迭代法可写出统一风格代码,层序遍历属广度优先搜索,借助队列实现,还给出C++代码及课后习题。
nuyoah♂
986
《代码随想录第十三天》——二叉树理论基础、递归遍历迭代遍历、统一迭代层序遍历
本文详细介绍了二叉树的种类、存储方式、遍历方法,包括递归遍历迭代遍历,以及统一迭代层序遍历实现。通过C++语言,深入探讨了二叉树的理论基础和相关算法。
-Michelangelo-
850
C++实现二叉树的前中后层序遍历递归迭代
本文详细介绍了如何使用C++实现二叉树的前序、中序、后序以及层序遍历,提供了递归迭代两种方法,并给出了具体的遍历结果。
小麦China
898
二叉树递归遍历 迭代遍历二叉树 层序遍历
该博客介绍了二叉树遍历方法。包括三种递归遍历(前序、中序、后序),其顺序略有不同;迭代遍历,借助栈实现前序、后序、中序遍历层序遍历,利用队列和二维数组实现,按层存储节点值。还给出了相应的 C++ 代码示例。
默默修炼的小趴菜
894
数据结构算法:二叉树——遍历与层序遍历
本文围绕二叉树遍历与层序遍历展开。介绍了二叉树递归迭代遍历方法,包括前序、中序、后序遍历。还给出层序遍历迭代递归方式,并通过多个题目,如二叉树层次遍历 II、右视图等,详细讲解层序遍历应用,同时分析了计算二叉树最小深度时不同 C++ 版本递归 lambda 函数的区别。
qq_44742941
1139
代码随想录C++ Day13 | 二叉树递归遍历迭代遍历,统一迭代层序遍历
本文深入探讨二叉树遍历方法,包括递归与迭代的前序、中序、后序遍历,以及层序遍历。通过代码实例解析不同遍历方式的实现技巧,如使用栈和队列辅助遍历,适用于算法面试和数据结构学习。
Aster2012_
1217
代码随想录 | Day15 | 二叉树:递归遍历&&迭代遍历&&层序遍历
本文详细介绍了二叉树递归与迭代遍历方法,包括前序、中序、后序及层序遍历,并提供了C++实现代码。
为了前进而后退,为了走直路而走弯路
958
代码随想录算法训练营第十三天 | 二叉树理论基础、递归遍历迭代遍历、统一迭代层序遍历
本文深入探讨了二叉树的理论基础,包括满二叉树、完全二叉树、二叉搜索树及平衡二叉搜索树的概念。详细讲解了递归遍历方法,包括前序、中序和后序遍历,并介绍了层序遍历实现技巧。同时,文章还对比了C++中map、set等容器的底层实现原理。
飞奥
1841
代码随想录算法训练营DAY15|C++二叉树Part.2|102.二叉树层序遍历、226.翻转二叉树、101. 对称二叉树
本文详细介绍了二叉树层序遍历算法,包括迭代法的伪代码和CPP实现,以及递归法的理解难点和逻辑。还涉及了翻转二叉树和对称二叉树的解题思路代码示例。
Che3rry
1551
代码随想录Day13|递归遍历迭代遍历、统一迭代层序遍历
本文详细讲解了二叉树的各种遍历方式,包括递归遍历迭代遍历、统一迭代层序遍历,并针对多个经典问题提供了具体解法。涵盖了前序、中序、后序遍历层序遍历应用场景和技术细节。
SleepyWhite001
695
代码随想录day14:二叉树01(理论基础,递归遍历迭代遍历,统一迭代层序遍历
本文全面介绍了二叉树的基础概念、存储方式、遍历方法及应用,包括递归迭代实现,并详细解析了二叉树层序遍历
里克曼不能慢
936
二叉树前序、中序、后序、层序遍历(递归、非递归、Morris遍历) C++语言实现
本文详细介绍了二叉树的多种遍历方法,包括层次、先序、中序、后序遍历及其递归迭代和Morris遍历实现,并提供了完整的C++代码示例。
zhangbw~
1390
数据结构算法锦集_C/C++
二叉树作为最典型的非线性结构,涵盖二叉搜索树(BST)的插入/删除/查找、AVL树红黑树的自平衡机制(旋转操作详解)、满二叉树与完全二叉树的数组存储技巧、前/中/后序递归与非递归遍历(借助显式栈模拟)
与二叉树代码模板.zip
文档《树与二叉树代码模板.doc》作为配套说明,不仅列出函数签名时间复杂度(如中序遍历递归O(n)时间/O(h)空间,非递归O(n)/O(h)),更会详解边界处理(空树、单节点、含null指针)、内存安全规范
等天晴i
计科数据结构实验\数据结构答案.rar
,答案中必定包含二叉链表存储结构定义、先/中/后序递归与非递归遍历(含栈模拟过程详解)、层序遍历(借助队列)、线索二叉树构造、哈夫曼树构建编码、二叉排序树(BST)的插入/删除/查找/平衡调整(可能延伸至
C/C++ 数据结构算法详解
),以及层序遍历(借助队列);更进一步涵盖线索二叉树的构造与遍历、哈夫曼树的构建最优编码应用、树与二叉树的相互转换、森林的遍历等高阶内容,为后续图论高级算法打下坚实基础。
孤飞俊驰
图解数据结构C++
、指针域指向关系、动态内存分配流程);再以时间轴动画式图解呈现前序/中序/后序递归遍历的调用栈展开回溯过程,同步标注每次访问、处理、返回对应的操作语义;对于层序遍历,则辅以队列状态变化图,清晰展现FIFO
TrampC
很经典的数据结构例题答案
、滑动窗口最大值(单调队列优化)、任务调度模拟等场景的应用;“链表”题目绝不止于单链表反转(递归与迭代双解)、环检测(Floyd判圈算法)、合并两个有序链表,更会延伸至复杂变体如带随机指针的链表深拷贝
常见算法面试题目有代码详解
高频题目覆盖前/中/后序遍历递归实现简洁直观,但迭代写法需借助显式栈模拟调用栈,体现对执行流程的深刻理解);层序遍历(BFS,需队列+分层标记技巧);二叉搜索树(BST)相关——验证BST合法性(中序遍历升序性质
个人收集的C++经典算法案例集
)、层序遍历(借助`队列.doc`中的STL queue或自实现循环队列)、线索化二叉树构造、AVL树平衡旋转(LL/LR/RR/RL四种情形的手动指针重连)、红黑树关键性质验证逻辑,以及经典应用如表达式树求值
用JavaScript实现的算法和数据结构,附详细解释和刷题指南.zip
、路径存储比对、以及Morris遍历空间O(1)解法;平衡二叉树(AVL)则需详解旋转操作的JS对象引用更新逻辑高度缓存维护。
嵌入式JunG
leetcode:leetcode个人题解
二叉树”部分绝非仅停留于前中后序遍历,而是构建完整认知框架递归三要素(终止条件、单层逻辑、返回值)到迭代统一模板(利用栈模拟递归+标志位区分访问阶段),再到层序遍历的BFS双队列/单队列+size
Rosie Lau