手撕二叉树遍历之中序遍历

本文旨在描述如何用迭代的方式一步一步实现二叉树中序遍历的过程!

我们先定义一个方法,参数为二叉树的根节点。方法内包含一个while循环。

private static void inOrderTraversal(TreeNode root) {
  while(condition) {
  }
}

还需要一个变量记录当前访问的节点,第一个访问的节点是根节点

private static void inOrderTraversal(TreeNode root) {
  TreeNode cur = root;
  while(condition) {
  }
}

while循环的条件是什么呢?答案是还有需要访问的节点,也就是说当前访问的节点不为空。先不考虑,我们如何得到每次循环需要访问的节点。

private static void inOrderTraversal(TreeNode root) {
  TreeNode cur = root;
  while(cur != null) {
  }
}

中序遍历是先中序遍历左子树,所以我们可以在每次主循环的开始,循环判断当前节点的左节点是否为空,如果不为空,则访问当前节点的左节点。

private static void inOrderTraversal(TreeNode root) {
  TreeNode cur = root;
  while(cur != null) {
    while (cur.left != null) {
       cur = cur.left;
    }
  }
}

通过当前代码,我们最终会找到二叉树的最左节点,也就是我们中序遍历需要访问的第一个节点。我们直接打印节点的值,表示我们访问过该节点。

private static void inOrderTraversal(TreeNode root) {
  TreeNode cur = root;
  while(cur != null) {
    while (cur.left != null) {
       cur = cur.left;
    }
    system.out.print(cur.val + " ");
  }
}

如果当前节点的右节点不为空,接着访问当前节点的右节点。此时,当前节点的右节点变成当前节点。

private static void inOrderTraversal(TreeNode root) {
  TreeNode cur = root;
  while(cur != null) {
    while (cur.left != null) {
      cur = cur.left;
    }
    System.out.print(cur.val + " ");
    if (cur.right != null) {
      cur = cur.right;
    } 
  }
}

通过当前代码,我们最终会得到一个没有左节点,也没有右节点的节点。我们无法通过左右节点得到其他可访问的节点。那是不是说循环就要结束了呢?当然不是,因为我们还没有遍历所有的节点。通过观察之前的循环,我们不难发现有部分节点我们只访问了他们的左节点,现在我们需要先访问这些节点再访问他们的右节点。但是由于二叉树没有指向父节点的引用,我们没有办法得到这些节点,所以我们需要将之前只访问过其左节点的那些节点保存起来。又由于我们访问这些节点的顺序跟之前循环的顺序正好相反,这符合栈先进后出的特性,所以我们使用栈来保存这些节点。

private static void inOrderTraversal(TreeNode root) {
  Stack<TreeNode> stack = new Stack<>();
  TreeNode cur = root;
  while(cur != null) {
    while (cur.left != null) {
      stack.push(cur);
      cur = cur.left;
    }
    System.out.print(cur.val + " ");
    if (cur.right != null) {
      cur = cur.right;
    } else {
    
    }
  }
}

如果栈不为空,我们从栈顶弹出节点。我们直接打印该节点的值,表示访问过该节点。如果该节点的右节点不为空,就访问该节点的右节点,并break当前循环。此时,该节点的右节点变成当前节点。如果该节点的右节点为空,我们继续从栈顶弹出节点。

private static void inOrderTraversal(TreeNode root) {
  Stack<TreeNode> stack = new Stack<>();
  TreeNode cur = root;
  while(cur != null) {
    while (cur.left != null) {
      stack.push(cur);
      cur = cur.left;
    }
    System.out.print(cur.val + " ");
    if (cur.right != null) {
      cur = cur.right;
    } else {
       while (!stack.isEmpty()) {
          TreeNode tmp = stack.pop();
          System.out.print(tmp.val + " ");
          if (tmp.right != null) {
           cur = tmp.right;
           break;
          }
        }
      }
    }
  }
}

当然,如果栈是空的,那就表示没有需要访问的节点了。我们可以设置当前节点为空,或直接break主循环。还有一种情况,就是栈不为空,但是栈中所有节点的右节点都为空,此时也表示没有需要访问的节点了。我们也需要设置当前节点为空,或直接break主循环。这两种情况可以用一个逻辑来覆盖。

private static void inOrderTraversal(TreeNode root) {
  Stack<TreeNode> stack = new Stack<>();
  TreeNode cur = root;
  while (cur != null) {
    while (cur.left != null) {
      stack.push(cur);
      cur = cur.left;
    }
    System.out.print(cur.val + " ");
    if (cur.right != null) {
      cur = cur.right;
    } else {
      cur = null;
      while (!stack.isEmpty()) {
        TreeNode tmp = stack.pop();
        System.out.print(tmp.val + " ");
        if (tmp.right != null) {
          cur = tmp.right;
          break;
        }
      }
    }
  }
}

中序遍历

(1)中序遍历左子树。

(2)访问根结点。

(3)中序遍历右子树。

<think>好的,我需要帮助用户查找如何使用C++实现二叉树的层序遍历,并给出示例代码。首先,我应该回忆层序遍历的基本概念。层序遍历属于广度优先搜索(BFS),通常使用队列来实现。用户提到参考了引用[1]和[2],其中引用[2]特别提到了C++和BFS的应用。 首先,我需要确保回答结构清晰,分步骤说明。可能需要先解释层序遍历的原理,然后介绍迭代法和递归法的实现方式。不过根据引用[1],层序遍历通常用迭代法(队列),而递归法可能不太直观,但用户可能也需要知道。不过引用[2]主要讨论BFS,也就是迭代法,所以应该重点放在迭代法上。 接下来,考虑C++的代码示例。需要定义二叉树的结构,比如使用结构体TreeNode,包含val、left和right指针。然后实现层序遍历函数,返回一个二维数组,每一层一个子数组。 迭代法的步骤:使用队列,首先将根节点入队。然后循环处理队列,记录当前层的节点数,循环该次数,取出节点,存入当前层的数组,并将其左右子节点入队。处理完一层后,将当前层数组加入结果。 递归法可能不太常见,但引用[1]提到递归法,所以可能需要实现。递归法需要维护当前层的层级,将节点值添加到对应的层级数组中。递归函数参数包括节点、当前深度和结果数组。对于每个节点,如果当前深度等于结果数组的大小,就新增一个子数组。然后递归处理左右子节点,深度加一。 然后需要确保代码符合C++语法,比如使用std::queue和std::vector。同时注意边界条件,比如根节点为空的情况。 最后,生成相关问题,比如层序遍历的应用场景、BFS和DFS的区别,或者如何用层序遍历二叉树深度等。 现在要检查引用是否正确,引用[1]提到层序遍历的迭代法和递归法,引用[2]强调BFS在层序遍历的应用,所以代码示例中的两种方法需要对应引用。同时,回答中需要自然地添加引用标识,比如在解释迭代法时引用[2],递归法引用[1]。 可能还需要注意用户提到的“LeetCode”,所以示例代码可能需要符合LeetCode的输入输出格式,比如返回vector<vector<int>>。 现在组织回答结构:先解释层序遍历,然后分迭代法和递归法,给出代码示例,并分析时间和空间复杂度,最后相关问题。</think>### C++实现二叉树序遍历的示例代码 层序遍历(BFS)按照树的层级逐层访问节点,常用于统计树的结构特征或按层处理数据。以下是两种实现方法: --- #### **方法1:迭代法(队列实现)** **核心思路**:利用队列的先进先出特性,逐层记录节点并处理子节点。 **步骤**: 1. 若根节点非空,将其加入队列。 2. 循环处理队列,每次记录当前层的节点数量。 3. 遍历当前层的每个节点,将其值存入结果数组,并将左右子节点加入队列。 4. 将当前层的结果加入最终结果列表。 **代码示例**: ```cpp #include <vector> #include <queue> using namespace std; struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} }; vector<vector<int>> levelOrder(TreeNode* root) { vector<vector<int>> result; if (!root) return result; queue<TreeNode*> q; q.push(root); while (!q.empty()) { int levelSize = q.size(); vector<int> currentLevel; for (int i = 0; i < levelSize; i++) { TreeNode* node = q.front(); q.pop(); currentLevel.push_back(node->val); if (node->left) q.push(node->left); if (node->right) q.push(node->right); } result.push_back(currentLevel); } return result; } ``` **复杂度分析**: - **时间复杂度**:$O(n)$,每个节点被访问一次。 - **空间复杂度**:$O(n)$,队列最多存储一层节点(最坏情况为满二叉树底层,约$n/2$节点)[^2]。 --- #### **方法2:递归法** **核心思路**:通过递归隐式维护层级信息,将节点值存入对应层级的数组。 **步骤**: 1. 递归访问节点时,记录当前深度。 2. 若当前深度等于结果数组长度,说明进入新层级,需新增子数组。 3. 将节点值存入对应层级的子数组,递归处理左右子节点。 **代码示例**: ```cpp void dfs(TreeNode* node, int depth, vector<vector<int>>& result) { if (!node) return; if (depth == result.size()) result.push_back(vector<int>()); result[depth].push_back(node->val); dfs(node->left, depth + 1, result); dfs(node->right, depth + 1, result); } vector<vector<int>> levelOrderRecursive(TreeNode* root) { vector<vector<int>> result; dfs(root, 0, result); return result; } ``` **复杂度分析**: - **时间复杂度**:$O(n)$,每个节点被访问一次。 - **空间复杂度**:$O(n)$,递归栈深度在最坏情况(树退化为链表)下为$O(n)$[^1]。 --- ### 关键点总结 | 方法 | 适用场景 | 空间复杂度优势 | |---------|----------------------|----------------------| | 迭代法 | 大规模数据、避免栈溢出 | 队列空间可控 | | 递归法 | 代码简洁、层级信息明确 | 依赖递归栈,可能溢出 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值