当前位置: 首页 > news >正文

线索二叉树

一 概念

线索二叉树(Threaded Binary Tree)是一种对二叉树的优化结构,主要解决传统二叉树遍历时需要借助栈或递归(额外空间开销)的问题。通过利用节点中的空指针(nullptr)存储遍历过程中的前驱或后继节点信息(称为 “线索”),可以实现无需额外空间的二叉树遍历

传统二叉树中,每个节点有两个指针(左孩子lchild、右孩子rchild)。对于一棵有n个节点的二叉树,共有2n个指针域,但其中只有n-1个指针被有效使用(连接子节点),剩余n+1个指针为空(nullptr)。
线索二叉树的核心思想是:将这些空指针利用起来,存储节点在某种遍历顺序(如中序、前序、后序)中的前驱或后继节点的地址,这些被重新利用的指针称为 “线索”。

为了区分一个指针是指向子节点还是线索,每个节点需要增加两个标志位:

  • ltag:左标志位
    • ltag = 0lchild指向左子节点
    • ltag = 1lchild是前驱线索(指向当前节点在遍历顺序中的前驱)
  • rtag:右标志位
    • rtag = 0rchild指向右子节点
    • rtag = 1rchild是后继线索(指向当前节点在遍历顺序中的后继)

二 代码 

1.线索二叉树结点

//线索二叉树结点
struct TreeNode {int data;TreeNode* left;TreeNode* right;bool ltag; //左线索,若为true,则left表示前驱结点,否则为左孩子结点bool rtag; //右线索,若为true,则right表示前驱结点,否则为右孩子结点TreeNode(int d) : data(d), left(nullptr), right(nullptr), ltag(false), rtag(false) {}
};

新增左右线索标志。若左线索为true,则left表示前驱结点,否则为左孩子结点。若右线索为true,则right表示后继结点,否则为右孩子结点。

2.构建线索二叉树

//构建线索二叉树(通过前序遍历序列构建)
TreeNode* CreateTree(vector<int>& preorder, int& index, int nullnode) {//若索引超出数组范围或数组索引处为空值nullnode,则令index++并返回空if (index >= preorder.size() || preorder[index] == nullnode) {index++;return nullptr;}TreeNode* node = new TreeNode(preorder[index++]); //为新结点分配内存node->left = CreateTree(preorder, index, nullnode); //递归构建左子树node->right = CreateTree(preorder, index, nullnode); //递归构建右子树return node; //返回当前结点
}

通过前序遍历序列来构建线索二叉树,若传入的索引超出数组范围或数组索引处为空值nullnode,则令Index++并返回空。为新结点分配内存,然后递归构建左右子树。

3.中序遍历线索化

//中序遍历线索化
TreeNode* pre = nullptr; //定义前驱结点void inorderThreading(TreeNode* node) {if (node == nullptr) return; //当传入结点为空时直接返回inorderThreading(node->left); //递归左子树,找到最左边孩子结点//传入结点的左子结点为空时,令其左指针指向前驱结点preif (node->left == nullptr) { node->left = pre;node->ltag = true;}//当前驱结点不为空且其右子结点为空时,令前驱结点的右指针指向当前结点if (pre && pre->right == nullptr) { pre->right = node;pre->rtag = true;}pre = node; //令前驱结点指向当前结点inorderThreading(node->right); //递归右子树
}

中序遍历线索化是最常用的线索化方式。首先在函数外定义一个全局结点变量pre表示前驱结点。

在函数体中,当传入的结点为空时直接返回。先递归左子树找到最左边孩子结点,当传入结点的左子结点为空时,令其左指针指向前驱结点pre,当前驱结点不为空且右子结点为空时,令前驱结点的右指针指向当前结点。最后令前驱结点指向当前结点并递归右子树。

4.中序遍历线索二叉树

//中序遍历线索二叉树
void inorderTraversal(TreeNode* root) {TreeNode* p = root;//找到中序遍历第一个节点(最左叶子)while (!p->ltag) { // 左线索为false表示有左子树,继续往左找p = p->left;}//循环遍历所有节点while (p != nullptr) {cout << p->data; //访问当前结点//找后继结点if (p->rtag) { //若rtag为true,则直接通过右线索找到后继结点p = p->right;}else { //若rtag为false,则找右子树的最左子结点p = p->right;while (!p->ltag) {p = p->left;}}}
}

首先找到中序遍历的第一个结点(最左叶子结点),左线索为false表示有左子树,继续往左找。

找到后循环遍历所有结点,每次循环先访问当前结点,然后找后继结点。若rtag为true,则直接通过右线索找到后继结点,若rtag为false,则找右子树的最左子结点。

5.释放内存

//释放线索二叉树内存
void FreeTree(TreeNode* root) {if (root == nullptr) return; // 空树直接返回// 保存当前节点的左右真实子节点(仅当标志为false时有效)TreeNode* leftChild = (root->ltag == false) ? root->left : nullptr;TreeNode* rightChild = (root->rtag == false) ? root->right : nullptr;// 递归释放左右子树(仅处理真实子节点)FreeTree(leftChild);FreeTree(rightChild);//释放当前结点delete root;root = nullptr; //指向空,避免野指针
}

若传入结点为空,直接返回。保存当前结点的左右真实结点,仅当标志为false时有效。接着递归释放左右子树,仅处理真实子结点。然后释放当前结点并让当前结点指向空以避免野指针。

三 线索二叉树优化之处

线索二叉树相比传统二叉树的核心优化在于空间利用率遍历效率,主要解决了传统二叉树在遍历和前驱 / 后继查找时的痛点。以下是具体优化点的对比分析:

1. 优化了空指针的浪费,节省存储空间

传统二叉树中,每个节点有两个指针(左 / 右孩子),但实际只有n-1个指针被有效使用(连接子节点),剩余n+1个指针为空(nullptr)。这些空指针未被利用,造成了空间浪费(以n个节点的二叉树为例,空指针占比约 50%)。

线索二叉树通过引入标志位ltag/rtag),将空指针重新定义为 “线索”,存储节点在某种遍历顺序(如中序)中的前驱或后继节点地址。原本被浪费的n+1个空指针被充分利用,实现了空间的高效复用。

2. 优化了遍历的空间复杂度,无需额外栈 / 递归

传统二叉树的遍历(如中序、前序、后序)依赖递归或显式栈结构:

  • 递归遍历:隐含使用调用栈,空间复杂度为O(h)h为树的高度,最坏情况O(n));
  • 迭代遍历:需手动维护栈,空间复杂度同样为O(h)

线索二叉树通过线索直接记录前驱 / 后继关系,遍历过程中无需额外栈或递归,空间复杂度降至O(1)(仅需一个指针变量)。例如,中序线索二叉树的遍历只需从最左节点开始,沿后继线索依次访问即可。

3. 优化了前驱 / 后继的查找效率

在传统二叉树中,查找某个节点的前驱或后继(如中序前驱 / 后继)需要重新遍历整棵树或回溯父节点,时间复杂度为O(n)(最坏情况)。

线索二叉树中,若节点的线索标志位为 1(ltag=1rtag=1),则其前驱 / 后继可直接通过线索指针获取(时间复杂度O(1));若标志位为 0(仍指向子节点),则仅需遍历子树即可找到前驱 / 后继(时间复杂度O(h),优于传统二叉树的O(n))。

总结:优化的本质是 “空间换时间” 的平衡

线索二叉树通过牺牲少量标志位(每个节点增加 2 个int标志),将原本浪费的空指针转化为遍历线索,最终实现:

  • 空间优化:复用空指针,减少额外存储(如栈空间);
  • 时间优化:遍历和前驱 / 后继查找的效率显著提升(尤其在需要频繁遍历的场景中)。

这一设计使得线索二叉树在编译器语法分析、文件系统目录遍历等需要高效顺序访问的场景中被广泛应用。

相关文章:

  • 服务器配置llama-factory问题解决
  • VR 南锣鼓巷:古老街区的数字化绘卷与沉浸式遨游​
  • 硬件选型:工控机的选择要素
  • 什么是 DDoS 攻击?从零到精通的全面解析
  • Tenorshare 4DDiG(数据恢复软件) v10.3.7.5 中文绿化版
  • shader中性能优化
  • 正则表达式实用指南:原理、场景、优化与引擎对比
  • 网络原理初识
  • VR制作软件用途(VR制作软件概述)
  • 【Python开源】深度解析:一款高效音频封面批量删除工具的设计与实现
  • Spring MVC 中Model, ModelMap, ModelAndView 之间有什么关系和区别?
  • Android Studio 模拟器配置方案
  • 【Python】Python项目中的依赖与配置:requirements.txt、setup.py、pyproject.toml 详解
  • 安防多协议接入/视频汇聚平台EasyCVR助力工地/工程/建筑施工领域搭建视频远程监控系统
  • 从零开始了解数据采集(二十四)——工业4.0讲解
  • Git查看某个commit的改动
  • 压入从0到Nvector(C++)
  • window 显示驱动开发-处理内存段(一)
  • 视频编解码学习9之照相机历史
  • AWS之存储服务
  • 洲际酒店:今年第一季度全球酒店平均客房收入同比增长3.3%
  • 人民日报评“组团退演出服”:市场经济诚信原则需全社会维护
  • 澎湃研究所“营商环境研究伙伴计划”启动
  • 云南临沧一行贿案金额认定比受贿案多41万,重审时检方变更金额起诉
  • 44岁街舞运动推广者、浙江省街舞运动协会常务理事钟永玮离世
  • 特色业务多点开花,苏州银行擦亮金融为民底色