C/C++数据结构之二叉树的遍历

概述

二叉树的遍历是指按照某种规则访问二叉树中所有节点的过程。遍历不仅是理解二叉树结构的基础,也是实现各种树操作的前提。二叉树的遍历顺序主要由根节点的访问时机决定,从而形成了三种深度优先遍历方式:前序、中序和后序。此外,还有基于层次的广度优先遍历方式:层序遍历。

深度优先遍历的特点是:沿着树的深度尽可能深地搜索,直到到达叶子节点或无法继续为止,然后回溯。广度优先遍历的特点是:按照树的层次从上到下、从左到右依次访问节点。

深度前序遍历

深度前序遍历的核心特点是:先访问根节点,然后再递归地遍历左子树和右子树。具体访问顺序如下:

1、访问当前节点(根节点)。

2、递归遍历左子树。

3、递归遍历右子树。

考虑下面的二叉树,其前序遍历的结果为:1 → 2 → 4 → 5 → 3。

cpp 复制代码
    1
   / \
  2   3
 / \
4   5

递归法是实现深度前序遍历最直观、最简洁的方式,直接体现了前序遍历的定义,示例代码如下。

cpp 复制代码
void PreorderTree(TreeNode* pRoot)
{
    if (pRoot == NULL)
    {
        return;
    }

    cout << pRoot->nData << " ";

    PreorderTree(pRoot->pLeft);

    PreorderTree(pRoot->pRight);
}

深度前序遍历的时间复杂度为O(n),其中n是树中节点的数量,每个节点恰好被访问一次。空间复杂度为O(h),其中h是树的高度。

深度中序遍历

深度中序遍历的核心特点是:在遍历完左子树后访问根节点,然后再遍历右子树。具体访问顺序如下:

1、递归遍历左子树。

2、访问当前节点(根节点)。

3、递归遍历右子树。

考虑下面的二叉树,其中序遍历的结果为:1 → 2 → 3 → 4 → 5 → 6 → 7。

cpp 复制代码
    4
   / \
  2   6
 / \ / \
1  3 5  7

深度前序遍历采用了递归的方法实现,这里我们采用迭代法来实现深度中序遍历,示例代码如下。

cpp 复制代码
void InorderTreeIterative(TreeNode* pRoot)
{
    stack<TreeNode*> nodeStack;
    TreeNode* pCurrent = pRoot;

    while (pCurrent != NULL || !nodeStack.empty())
    {
        // 一直向左走到底,将路径上的节点压入栈
        while (pCurrent != NULL)
        {
            nodeStack.push(pCurrent);
            pCurrent = pCurrent->pLeft;
        }

        // 此时pCurrent为空,说明到达最左节点
        pCurrent = nodeStack.top();
        nodeStack.pop();

        // 访问该节点
        cout << pCurrent->nData << " ";

        // 转向右子树
        pCurrent = pCurrent->pRight;
    }
}

深度后序遍历

深度后序遍历的核心特点是:最后访问根节点,在遍历完左右子树之后才访问当前节点。具体访问顺序如下:

1、递归遍历左子树。

2、递归遍历右子树。

3、访问当前节点(根节点)。

考虑下面的二叉树,其后序遍历的结果为:4 → 5 → 2 → 3 → 1。

cpp 复制代码
    1
   / \
  2   3
 / \
4   5

使用递归法实现深度后续遍历相对比较简单,可参考下面的示例代码。

cpp 复制代码
void PostorderTree(TreeNode* pRoot)
{
    if (pRoot == NULL) 
    {
        return;
    }

    PostorderTree(pRoot->pLeft);

    PostorderTree(pRoot->pRight);

    cout << pRoot->nData << " ";
}

广度层序遍历

广度层序遍历的核心特点是:按层次访问,从根节点开始,逐层向下,每层从左到右访问所有节点。具体访问顺序如下:

1、访问第1层(根节点)。

2、访问第2层(根的子节点)。

3、访问第3层(第2层节点的子节点)。

4、依此类推,直到最后一层。

考虑下面的二叉树,其层序遍历的结果为:1 → 2 → 3 → 4 → 5 → 6 → 7。

cpp 复制代码
    1
   / \
  2   3
 / \   \
4   5   6
     \
      7

广度层序遍历推荐使用迭代法来实现,最自然的实现方式是使用队列数据结构,遵循"先进先出"的原则,示例代码如下。

cpp 复制代码
void LevelOrder(TreeNode* pRoot)
{
    if (pRoot == NULL)
    {
        return;
    }

    queue<TreeNode*> nodeQueue;
    nodeQueue.push(pRoot);

    while (!nodeQueue.empty())
    {
        // 取出队首节点
        TreeNode* pCurrent = nodeQueue.front();
        nodeQueue.pop();

        // 访问当前节点
        cout << pCurrent->nData << " ";

        // 将左子节点入队(如果存在)
        if (pCurrent->pLeft != NULL)
        {
            nodeQueue.push(pCurrent->pLeft);
        }

        // 将右子节点入队(如果存在)
        if (pCurrent->pRight != NULL)
        {
            nodeQueue.push(pCurrent->pRight);
        }
    }
}
相关推荐
LayZhangStrive2 小时前
数据结构与算法 - 堆排序
java·数据结构·算法·排序算法·堆排序·大根堆
cpp_learner2 小时前
C++ 实现责任链模式(Chain of Responsibility):从一堆 if-else 到可插拔的处理管道
c++·设计模式
一木 之林2 小时前
五、C++ 新特性、关键字与编译原理(进阶)(一)
c语言·开发语言·c++
2601_962298272 小时前
交易开拓者通常使用什么编程语言?这种语言如何帮助自动化交易?
java·c++·python·编程语言·自动化交易
码匠许师傅2 小时前
【设计模式精讲】23.备忘录模式(Memento)
c++·设计模式·软件工程·uml·备忘录模式
leikooo2 小时前
ARTS 0906: 辅助栈记录每层最小值、OSI 模型从未真正落地与与其被动等待被颠覆不如主动设计规则
数据结构·人工智能·tcp/ip
键盘会跳舞3 小时前
【C++多线程】thread_local 深度剖析:线程局部存储的生命周期与实现
c++·多线程·thread_local
whitelbwwww3 小时前
c++ 多线程
开发语言·c++·算法
不会就选b3 小时前
数据结构之数&&二叉树(一)
数据结构