目录

TBT 的存储结构

线索化


如果一棵二叉树,所有原本为空的右孩子改为指向该结点的中序遍历的后继,所有原本为空的左孩子改为指向该结点的中序遍历的前驱,那么修改后的二叉树被称为 线索二叉树 (Threaded binary tree, TBT)。指向前驱、后继的指针被称为线索,对二叉树以某种遍历顺序进行扫描并为每个结点添加线索的过程称为二叉树的 线索化 ,进行线索化的目的是为了加快查找二叉树中某节点的前驱和后继的速度。

TBT 能线性地遍历二叉树,从而比递归的中序遍历更快。使用 TBT 也能够方便的找到一个结点的父结点,这比显式地使用父结点指针或者栈效率更高。这在栈空间有限,或者无法使用存储父节点的栈时很有作用。

TBT 的存储结构

如果一棵二叉树线索化之后,需要分辨哪些是线索哪些是边,因此我们不得不修改数据结构,使得有 field 来指示该结点的左或右孩子是否是线索。

该节剩下内容将作为扩展部分,可以进行选择性阅览。

我们改写为代码,由于 tag 实际上至于要 1 bit 就能指示线索,这时候 C / C++ 的优势就体现出来了,我们可以通过 位域 限制 tag 的大小,并将两个 tag 合并在 1 Byte 上来减少结构体空洞带来的内存浪费。

// 假设运行在 UNIX-like 64 bit OS 之上
template <class Element>
struct ThreadedBinaryTreeNode {Element data; // 占用 ?? byte// 产生 ?? bit 空洞int ltag : 1;int rtag : 1;// 产生 62 bit 空洞ThreadedBinaryTreeNode* left;   // 占用 8 byte// 如果 rtag 在这里定义,数据结构大小将增加 8 byte,其中包含 63 bit 的空洞ThreadedBinaryTreeNode* right;  // 占用 8 byte
};

在之前的内容中,所有代码尽量都避免 C / C++ 的一些深度的语言特性,来避免读者因为编程语言的特性而带来的困扰。但是下面这个例子,将展示 C / C++ 因为其底层、灵活而展示出的强大。

在 LP64 模型下指针大小为 64 bit,从堆上分配来的内存的地址,起始地址能够被其最宽的成员大小整除。那么含有指针的 TreadedBinaryTreeNode 在分配时其地址可以被 8 byte 整除,这是什么概念的,就是其地址的 低 3 bit 一定为 0 。我们可以充分利用这 3 bit,在不改变二叉树结点结构的情况下,辨别该结点是否是线索。如此整个结构体大小缩小了 8 byte。其实这个技巧在很多 C 代码中都有使用,甚至你可以考虑将结构体空洞废物利用起来,或者 C 的宏编程,这些 奇技淫巧 威力强大但降低了代码的可读性。

使用最低 3 bit 存储状态,那么我们在使用时就不能直接使用指针了,那么下列函数可能会对你使用这 3 bit 有帮助。

enum NodeStatus {LINK,THREAD,
};
constexpr uintptr_t HIDE_BIT = 3;
inline BinaryTreeBaseNode* get_node(BinaryTreeBaseNode* node) {return reinterpret_cast<BinaryTreeBaseNode*>(reinterpret_cast<uintptr_t>(node) & ~HIDE_BIT);
}
inline BinaryTreeBaseNode* get_left(BinaryTreeBaseNode* node) {return get_node(get_node(node)->left);
}
inline BinaryTreeBaseNode* get_right(BinaryTreeBaseNode* node) {return get_node(get_node(node)->right);
}
inline int get_status(BinaryTreeBaseNode* node) {return reinterpret_cast<uintptr_t>(node) & HIDE_BIT;
}
inline void set_left_link(BinaryTreeBaseNode* node, BinaryTreeBaseNode* left) {node->left = left;
}
inline void set_left_thread(BinaryTreeBaseNode* node, BinaryTreeBaseNode* left) {node->left = reinterpret_cast<BinaryTreeBaseNode*>(reinterpret_cast<uintptr_t>(left) | NodeStatus::THREAD);
}
inline void set_right_link(BinaryTreeBaseNode* node, BinaryTreeBaseNode* right) {node->right = right;
}
inline void set_right_thread(BinaryTreeBaseNode* node, BinaryTreeBaseNode* right) {node->right = reinterpret_cast<BinaryTreeBaseNode*>(reinterpret_cast<uintptr_t>(right) | NodeStatus::THREAD);
}
inline bool is_thread(BinaryTreeBaseNode* node) {return get_status(node) == NodeStatus::THREAD;
}

线索化

线索化时需要记录结点的前驱,如果你仔细观察 Morris Traversal,你可能会发现, Morris Traversal 是在构建一部分线索二叉树。

template <class Element>
void inorder_threading(ThreadedBinaryTreeNode<Element>* root) {auto curr = root, prev = root;while (curr != nullptr) {if (curr->left == nullptr) {curr->left = prev;curr->ltag = NodeStatus::THREAD;prev = curr;goto RIGHTING;}prev = curr->left;while (prev->right != nullptr && prev->right != curr) {prev = prev->right;}if (prev->right == nullptr) {prev->right = curr;prev->rtag = NodeStatus::THREAD;curr = curr->left;continue;}prev = prev->right;RIGHTING:curr = curr->right;}// 由于 root 的后继的左线索指向不正确,需要对其进行修正if (root->right != nullptr) {curr = root->right;while (curr->ltag == NodeStatus::LINK) {curr = curr->left;}curr->left = root;curr->ltag = NodeStatus::THREAD;}// 由于中序遍历第一个结点的左线索指向不正确,将其置空。其最后一个结点同样也是置空的curr = root;while (curr->ltag == NodeStatus::LINK) {curr = curr->left;}curr->left = nullptr;curr->ltag = NodeStatus::LINK;
}

【数据结构与算法分析】0基础带你学数据结构与算法分析09--线索二叉树 (TBT)相关推荐

  1. 视频教程-【跟一夫学设计】从0基础到精通学全套coreldraw x7轻松掌握CDR基础加案例学习视频教程-CorelDraw

    [跟一夫学设计]从0基础到精通学全套coreldraw x7轻松掌握CDR基础加案例学习视频教程 中国电商服务联盟品牌讲师.中国国际互联网节品牌顾问. 12年视觉设计经验,5年视觉讲师经验.电商品牌视 ...

  2. 0基础用动画学Python,在闲余时间月赚2k-10k

    前几天,和大学几个兄弟聚会.聊到在大家最近的工作怎么样?在北京工作的大学兄弟魏明聊起他的工作,副业赚的钱比工资还要多! 我们几个马上凑过来:副业做的是什么啊? 聊了会才知道,原来他半年前学习了Pyth ...

  3. 【跟一夫学设计】从0基础到精通学全套coreldraw x7轻松掌握CDR基础加案例学习视频教程-王诚-专题视频课程...

    [跟一夫学设计]从0基础到精通学全套coreldraw x7轻松掌握CDR基础加案例学习视频教程-194人已学习 课程介绍         0基础学习coreldraw x7 掌握CDR各项基础技能, ...

  4. 0基础该如何学Python?这些方法你必须了解

    往期好文推荐 学习Python不需要程基础? 0基础不用怕,从0到1轻松教你入门Python 由于python在人工智能,机器学习,大数据,数据分析,网络爬虫,全方位的技能特点,是非常适合初学者入门和 ...

  5. 0基础学python有多难-0基础纯小白学Python,请注意这2个坑

    0基础的纯小白学Python有哪些坑需要避免?有没有适合小白的Python教材推荐? 很多纯0基础,毫无编程经验的小白在学习Python的过程中总是无法坚持下来,究其原因,其实就是因为毫无经验,方向不 ...

  6. 0基础半路转行学IT还来得及吗?

    有网友在网上提问: 转行IT学编程能成为技术大牛吗? 收到诸多网友评论的同时,也被不少网友反问: 1.成不了技术大牛,就不学编程了吗? 2.学习是一辈子的事情,哪里来的半路出家?25岁学编程,35岁的 ...

  7. Carson带你学数据结构:手把手带你了解 ”图“ 所有知识!(含DFS、BFS)

    前言 本文主要讲解 数据结构中的图 结构,包括 深度优先搜索(DFS).广度优先搜索(BFS).最小生成树算法等,希望你们会喜欢. 目录 1. 简介 数据结构的中属于 圆形结构 的逻辑结构 具体介绍如 ...

  8. 数据结构知识点总结_大牛带你学 | 考研数据结构中线性表中顺序结构的知识点总结...

    前言 我们都知道,数据结构中逻辑结构可以划分为线性结构(线性表)与非线性结构两大类. 而存储结构指的是数据元素在计算机中的存储及其逻辑关系的表现,也就是在计算机当中对逻辑结构的表示. 线性表的存储结构 ...

  9. 0基础也能学!软件测试自学路线(附带学习资料)

    其实我的学习过程是比较艰辛的,大学学的计算机专业,出来找工作没找到对口因为大学学的其实蛮片面的,后来朋友介绍去了一家it公司做文职,拿着一个月两三千的工资,别说找女朋友了,养活自己都困难,然后就跟自己 ...

最新文章

  1. 【windows】cmd中的help无法使用的解决方法
  2. 机器学习必知的8大神经网络架构和原理
  3. WebGIS中地图恢复初始位置及状态
  4. Spring创建对象的三种方式以及创建时间
  5. python矩阵相加_【python矩阵相加怎么做,这可是证明python功能的大好机会】- 环球网校...
  6. eclipse launching workspace太慢的解决方法
  7. JSP与Servelt的区别
  8. Python:langdetect和langid检测语言类型
  9. Oracle to_char() to_date() to_number()函数
  10. 判断字符串出栈合法性
  11. 性能测试第一步——查看设备的硬件配置
  12. python新手难点_汇总初学python时的28个操作难点(新手必看篇)
  13. 【渝粤教育】国家开放大学2018年秋季 1070t组织行为学 参考试题
  14. NSDate 时间戳与字符串转换
  15. MySQL命令执行脚本文件
  16. Unity使用脚本模拟Button按下
  17. [angular1.6]Error: “transition superseded“ ui-router 在angular1.6 报错误问题解决
  18. 浅谈中国2050年人均受教育年限问题
  19. 为此计算机上所有用户安装此加载项,win7打开特定网站提示“控件无法安装,windows已阻止此软件因为无法验证发行者”怎么办...
  20. 完美的word转pdf

热门文章

  1. 第7章-1 词频统计 (30 分)
  2. 空天地海一体化网络切片研究综述
  3. JS 格式化价格保留两位小数
  4. 小程序价格四舍五入,保留2位小数
  5. iOS开发 HealthKit初步探索
  6. JAVA使用Localtime_Java LocalTime plus()用法及代码示例
  7. Linux:ftp匿名用户无法登录,530 Permission denied.Login failed.
  8. ROS学习笔记(十三) TF介绍(一)
  9. 初识C语言 —— 第一篇bolg
  10. ADB Fastboot Recovery Hboot Bootloader介绍