考研数据结构之二叉树(二):二叉树的遍历与线索二叉树(包含真题实战)

考研数据结构之二叉树(二):二叉树的遍历与线索二叉树

在上一篇文章中,我们详细探讨了二叉树的定义和存储结构。本文将深入讲解二叉树的核心操作——遍历,以及其重要应用形式之一——线索二叉树。这些内容不仅是考研的重点,也是实际编程中常用的重要知识点。

下期预告:后续文章将进一步探讨二叉树的其他高级应用(如森林、平衡二叉树、哈夫曼树等)


一、二叉树的遍历

1. 遍历的概念

遍历是指按照某种规则访问二叉树中的每个节点且仅访问一次的过程。根据访问根节点的顺序不同,遍历分为以下三种基本方式:

  • 先序遍历(Pre-order Traversal)
    访问顺序:根节点 → 左子树 → 右子树。
    特点:可以用来复制二叉树或构造表达式树。

  • 中序遍历(In-order Traversal)
    访问顺序:左子树 → 根节点 → 右子树。
    特点:对于二叉搜索树,中序遍历结果是有序的。

  • 后序遍历(Post-order Traversal)
    访问顺序:左子树 → 右子树 → 根节点。
    特点:常用于释放二叉树内存(如删除整棵树)。

此外,还有一种特殊的遍历方式——层次遍历(Level-order Traversal),也称广度优先遍历(BFS)。它按照从上到下、从左到右的顺序逐层访问节点。


2. 遍历的实现

(1)递归实现

递归实现简单直观,直接按照遍历顺序编写代码。以C语言为例:

void PreOrder(BiTree T) {
    if (T) {
        printf("%d ", T->data); // 访问根节点
        PreOrder(T->lchild);   // 递归访问左子树
        PreOrder(T->rchild);   // 递归访问右子树
    }
}

void InOrder(BiTree T) {
    if (T) {
        InOrder(T->lchild);     // 递归访问左子树
        printf("%d ", T->data); // 访问根节点
        InOrder(T->rchild);     // 递归访问右子树
    }
}

void PostOrder(BiTree T) {
    if (T) {
        PostOrder(T->lchild);   // 递归访问左子树
        PostOrder(T->rchild);   // 递归访问右子树
        printf("%d ", T->data); // 访问根节点
    }
}
(2)非递归实现

非递归实现通常借助栈完成。例如,中序遍历的非递归实现如下:

void InOrderNonRecursive(BiTree T) {
    Stack S;
    InitStack(S);
    BiTree p = T;
    while (p || !IsEmpty(S)) {
        if (p) {
            Push(S, p);       // 当前节点入栈
            p = p->lchild;    // 继续访问左子树
        } else {
            Pop(S, &p);       // 弹出栈顶元素
            printf("%d ", p->data); // 访问节点
            p = p->rchild;    // 转向右子树
        }
    }
}
(3)层次遍历

层次遍历使用队列实现,按照“先进先出”原则逐层访问节点。

void LevelOrder(BiTree T) {
    Queue Q;
    InitQueue(Q);
    EnQueue(Q, T);
    while (!IsEmpty(Q)) {
        BiTree node;
        DeQueue(Q, &node);
        printf("%d ", node->data); // 访问当前节点
        if (node->lchild) EnQueue(Q, node->lchild);
        if (node->rchild) EnQueue(Q, node->rchild);
    }
}

二、线索二叉树

1. 线索二叉树的定义

普通二叉树中,空指针域(NULL)未被利用。为了提高遍历效率,可以通过线索化将空指针指向某些特定节点(如前驱或后继),从而减少遍历时对栈的依赖。这种经过改造的二叉树称为线索二叉树

线索二叉树分为以下几种类型:

  • 先序线索二叉树:按先序遍历规则线索化。
  • 中序线索二叉树:按中序遍历规则线索化。
  • 后序线索二叉树:按后序遍历规则线索化。

2. 线索二叉树的存储结构

为实现线索化,需在节点结构中增加两个标志位字段:

typedef struct ThreadNode {
    int data;
    struct ThreadNode *lchild, *rchild;
    int ltag, rtag; // 0表示指针指向子节点,1表示指向前驱或后继
} ThreadNode, *ThreadTree;
  • ltag = 0lchild 指向左子节点;
  • ltag = 1lchild 指向前驱节点;
  • rtag = 0rchild 指向右子节点;
  • rtag = 1rchild 指向后继节点。

3. 中序线索化算法

以下是中序线索化的实现:

ThreadNode *pre = NULL; // 全局变量,记录当前节点的前驱

void InThread(ThreadTree T) {
    if (T) {
        InThread(T->lchild); // 递归线索化左子树
        if (!T->lchild) {    // 若左子树为空
            T->ltag = 1;     // 设置ltag为1
            T->lchild = pre; // 左指针指向前驱
        }
        if (pre && !pre->rchild) { // 若前驱的右子树为空
            pre->rtag = 1;         // 设置rtag为1
            pre->rchild = T;       // 前驱的右指针指向当前节点
        }
        pre = T;                   // 更新前驱为当前节点
        InThread(T->rchild);       // 递归线索化右子树
    }
}

三、真题解析

1. 遍历序列还原二叉树

题目(2021年真题,):

已知某二叉树的先序序列为 ABDCE,中序序列为 DBAEC,请画出该二叉树并写出后序序列。

解析

  1. 先序A为根节点,左子树先序为 BD,右子树先序为 CE
  2. 中序A左侧为左子树 DB,右侧为右子树 EC
  3. 递归构建
        A
       / \
      B   C
     /   /
    D   E
    
  4. 后序序列D B E C A

2. 层次遍历的应用

题目

已知二叉树的层次遍历序列为 A B C D E # #,请写出其中序遍历序列。

解析

  • 还原二叉树
        A
       / \
      B   C
     / \
    D   E
    
  • 中序遍历D B E A C

四、总结

  • 遍历是二叉树的核心操作,掌握递归与非递归实现方法至关重要。
  • 线索二叉树通过利用空指针提升遍历效率,是一种重要的优化手段。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

老北京儿码农

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值