数据结构|二叉树层序遍历 & 线索二叉树:吃透二叉树进阶核心考点

看完上一篇二叉树非递归深度遍历,相信大家已经搞定了前序、中序、后序的高频手撕代码。

但二叉树的面试和刷题考点,远不止深度遍历!

除了「一条路走到黑」的深度遍历,还有算法笔试必考层序遍历(广度优先遍历) ;同时还有解决二叉树空间冗余、遍历低效的进阶结构------线索二叉树

很多同学刷题只会普通遍历,不懂层序、不会线索二叉树,遇到二叉树进阶题型直接丢分。

今天这篇文章,一次性讲透两大进阶核心知识点,零基础能看懂,面试能直接用


一、前置区分:深度遍历 vs 层序遍历

我们之前学的前、中、后序遍历,都属于深度优先遍历(DFS) :优先往树的深层走,走到尽头再回溯,核心依赖实现。

而今天的层序遍历(BFS,广度优先遍历),逻辑完全相反:

从上到下、从左到右,一层一层遍历整棵树,不深挖子树,先遍历完当前层所有节点,再遍历下一层。

它的核心工具也彻底改变:放弃栈,只用队列(Queue),利用队列「先进先出」的特性,完美适配层级遍历逻辑。


二、二叉树层序遍历:笔试刷题天花板考点

1. 核心原理

队列先进先出,先存入当前层所有节点,依次取出节点访问,同时将该节点的左、右子节点依次入队,循环往复,直到队列为空,即可实现层级有序遍历。

2. 详细执行步骤

  1. 判空:如果根节点为空,直接结束遍历;

  2. 初始化队列,将根节点入队;

  3. 循环判断队列是否为空,每一轮循环处理一整层节点

  4. 获取当前队列长度,即为当前层的节点个数;

  5. 依次弹出队首节点,访问节点值,同时将该节点的左、右子节点(非空)入队;

  6. 当前层遍历完成后,进入下一层循环,直至队列清空。

3. C++ 完整可运行代码

cpp 复制代码
#include <iostream>
#include <queue>
#include <vector>
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>> res;
    if (root == nullptr) return res;

    queue<TreeNode*> q;
    q.push(root);

    while (!q.empty()) {
        // 当前层节点数量
        int levelSize = q.size();
        vector<int> levelData;

        // 遍历当前层所有节点
        for (int i = 0; i < levelSize; i++) {
            TreeNode* cur = q.front();
            q.pop();
            levelData.push_back(cur->val);

            // 左右子节点入队,先左后右保证顺序
            if (cur->left != nullptr) q.push(cur->left);
            if (cur->right != nullptr) q.push(cur->right);
        }
        res.push_back(levelData);
    }
    return res;
}

// 测试主函数
int main() {
    // 构建测试二叉树
    TreeNode* root = new TreeNode(1);
    root->left = new TreeNode(2);
    root->right = new TreeNode(3);
    root->left->left = new TreeNode(4);
    root->left->right = new TreeNode(5);

    vector<vector<int>> ans = levelOrder(root);
    // 打印层序遍历结果
    for (auto &level : ans) {
        for (int num : level) {
            cout << num << " ";
        }
        cout << endl;
    }
    return 0;
}

4. 核心总结与适用场景

核心口诀:队列存层、逐层出队、子节点入队、层层迭代。

高频适用场景:二叉树广度搜索、二叉树最大/最小深度、二叉树每层平均值、锯齿层序遍历等刷题经典题型,全部基于该核心逻辑改造。

相比于深度遍历,层序遍历更适合层级统计、最短路径类问题,是算法笔试的重中之重。


三、普通二叉树的致命缺陷:为什么需要线索二叉树?

学完四种基础遍历(前、中、后、层序),很多同学会以为二叉树已经学透了,但普通二叉树存在两个严重短板:

1. 空间严重冗余

一棵 n 个节点的二叉树,必然有 n+1 个空指针域,大量空指针浪费内存空间,数据量越大,浪费越严重。

2. 遍历必须依赖栈/递归,效率有限

普通二叉树想要遍历,要么递归(有栈溢出风险),要么手动栈(代码繁琐),且无法直接找到某个节点的前驱、后继节点,每次查询都需要重新遍历。

为了解决这两个问题,线索二叉树 应运而生:利用闲置的空指针,记录节点前驱、后继,不用栈、不用递归,即可高效遍历二叉树


四、线索二叉树:原理、定义与核心规则

1. 核心概念

线索二叉树,本质是优化后的二叉树:将节点的空左指针指向遍历前驱节点,空右指针指向遍历后继节点,这些指针被称为「线索」。

为了区分普通指针和线索指针,需要给节点新增两个标记位:

  • ltag:0 表示左指针指向左孩子,1 表示左指针是前驱线索

  • rtag:0 表示右指针指向右孩子,1 表示右指针是后继线索

2. 全新节点结构定义(C++)

cpp 复制代码
// 线索二叉树节点定义
struct ThreadTreeNode {
    int val;
    ThreadTreeNode* left;
    ThreadTreeNode* right;
    // 标记位:0-孩子指针,1-线索指针
    int ltag, rtag;
    ThreadTreeNode(int x) : val(x), left(nullptr), right(nullptr), ltag(0), rtag(0) {}
};

3. 核心规则(以中序线索二叉树为例)

我们最常用、面试最常考的是中序线索二叉树,规则如下:

  • 节点左孩子为空:left 指针指向中序遍历的前驱节点,ltag=1

  • 节点右孩子为空:right 指针指向中序遍历的后继节点,rtag=1

  • 有孩子的节点,指针正常指向左右孩子,标记位为 0


五、中序线索二叉树:构建 + 遍历完整实现

1. 构建核心思路

基于中序遍历逻辑,递归遍历二叉树,用全局前驱节点记录上一个访问节点,每当遇到空指针,就建立前驱、后继线索关联。

2. 完整C++代码实现

cpp 复制代码
#include <iostream>
using namespace std;

// 线索二叉树节点定义
struct ThreadTreeNode {
    int val;
    ThreadTreeNode* left;
    ThreadTreeNode* right;
    int ltag, rtag;
    ThreadTreeNode(int x) : val(x), left(nullptr), right(nullptr), ltag(0), rtag(0) {}
};

// 全局前驱节点,记录上一个访问的节点
ThreadTreeNode* pre = nullptr;

// 中序遍历构建线索二叉树
void createThreadTree(ThreadTreeNode* cur) {
    if (cur == nullptr) return;

    // 1. 递归遍历左子树
    createThreadTree(cur->left);

    // 2. 处理当前节点
    // 左孩子为空,建立前驱线索
    if (cur->left == nullptr) {
        cur->ltag = 1;
        cur->left = pre;
    }
    // 上一个节点右孩子为空,建立后继线索
    if (pre != nullptr && pre->right == nullptr) {
        pre->rtag = 1;
        pre->right = cur;
    }
    // 更新前驱节点为当前节点
    pre = cur;

    // 3. 递归遍历右子树
    createThreadTree(cur->right);
}

// 线索二叉树中序遍历(无栈、无递归)
void threadTreeTraverse(ThreadTreeNode* root) {
    ThreadTreeNode* cur = root;
    // 循环遍历所有节点
    while (cur != nullptr) {
        // 1. 找到最左侧起始节点
        while (cur->ltag == 0) {
            cur = cur->left;
        }
        // 访问当前节点
        cout << cur->val << " ";

        // 2. 沿着后继线索遍历
        while (cur->rtag == 1 && cur->right != nullptr) {
            cur = cur->right;
            cout << cur->val << " ";
        }
        // 转向右子树
        cur = cur->right;
    }
}

// 测试主函数
int main() {
    // 构建测试二叉树
    ThreadTreeNode* root = new ThreadTreeNode(1);
    root->left = new ThreadTreeNode(2);
    root->right = new ThreadTreeNode(3);
    root->left->left = new ThreadTreeNode(4);
    root->left->right = new ThreadTreeNode(5);

    // 构建线索二叉树
    createThreadTree(root);
    // 遍历线索二叉树
    cout << "中序线索二叉树遍历结果:";
    threadTreeTraverse(root);

    return 0;
}

3. 线索二叉树核心优势

  • 节省空间:复用空指针,消除内存冗余,无需额外存储前驱后继信息

  • 遍历高效:遍历无需栈、无需递归,时间复杂度 O(n),无栈溢出风险

  • 查询便捷:可直接获取任意节点的前驱、后继节点,无需重复遍历


六、面试高频考点终极梳理

  1. 层序遍历和深度遍历的核心区别?

深度遍历(DFS)依靠栈,纵向深挖,优先遍历子树;层序遍历(BFS)依靠队列,横向遍历,优先遍历同级节点,适配层级类、最短路径类问题。

  1. 层序遍历的核心框架是什么?

队列存节点、循环取层、逐层遍历、子节点入队,是所有BFS算法的基础模板。

  1. 线索二叉树的作用是什么?

解决普通二叉树空指针冗余问题,实现非递归、无栈高效遍历,快速查找节点前驱后继。

  1. ltag、rtag 标记位的意义?

区分指针类型,避免遍历时分不清指向的是孩子节点,还是线索前驱/后继节点。


七、全文总结

至此,二叉树深度遍历 + 层序遍历 + 线索二叉树三大核心体系全部讲透:

  • 深度遍历(前/中/后):栈实现,纵向遍历,适配常规刷题

  • 层序遍历:队列实现,横向遍历,笔试高频必考

  • 线索二叉树:优化空间与遍历效率,解决原生二叉树缺陷,进阶核心考点

这三套知识点相辅相成,覆盖了本科考试、算法刷题、求职面试90%以上的二叉树考点,熟练掌握即可轻松应对所有二叉树基础+进阶题型!

后续会更新二叉树平衡、哈夫曼树等高阶内容,想学透数据结构可以持续关注!

相关推荐
凯瑟琳.奥古斯特1 小时前
力扣2760 C++滑动窗口解法
数据结构·c++·算法·leetcode·职场和发展
此生决int3 小时前
快速复习之数据结构篇——二叉树(二)
数据结构
凯瑟琳.奥古斯特3 小时前
BFS解力扣1654最短跳跃次数
数据结构·算法·广度优先
m0_629494733 小时前
LeetCode 热题 100-----23.反转链表
数据结构·算法·leetcode·链表
无限进步_3 小时前
【C++】从红黑树到 map 和 set:封装设计与迭代器实现
开发语言·数据结构·数据库·c++·windows·github·visual studio
2501_921960853 小时前
协同本体论·离散动力学模拟:两个官方版本
数据结构·重构
Allen_LVyingbo4 小时前
面向医疗群体智能的协同诊疗与群体决策支持系统(上)
数据结构·数据库·人工智能·git·python·动态规划
满天星83035774 小时前
定长内存池ObjectPool
数据结构·c++·算法·链表
努力努力再努力wz4 小时前
【Qt入门系列】第一个 Qt Widgets 程序:项目创建、UI 文件、Hello World、对象树与 qDebug 日志
java·c语言·开发语言·数据结构·c++·qt·ui