线索二叉树是一种对普通二叉树进行扩展的数据结构,旨在提高遍历效率。其核心思想是利用二叉链表中大量的空指针域来保存节点在某种遍历顺序下的前驱和后继信息。
-
定义:
线索二叉树(Threaded Binary Tree)是在二叉树的基础上,通过修改空指针使其指向该节点在某种遍历序列(如先序、中序或后序)中的前驱或后继节点,从而形成的特殊二叉树。这种被“线索化”的结构使得不使用递归或栈也能高效地进行遍历操作。 -
存储结构:
每个节点包含五个部分:ltag:左标志位,0 表示lchild指向左孩子,1 表示lchild指向前驱;lchild:左指针;data:数据域;rchild:右指针;rtag:右标志位,0 表示rchild指向右孩子,1 表示rchild指向后继。
结构示例如下(以中序线索二叉树为例):
A / \ B C / \ D E中序遍历为:D → B → E → A → C
线索化后,D 的rchild(原为空)将指向 B(后继),B 的lchild(若为空)将指向前驱 D,以此类推。 -
线索化过程:
在进行中序遍历时,设置一个全局指针pre初始为null,当访问当前节点p时:- 若
pre != null且pre的右指针为空,则将其rtag设为 1,并让rchild指向p(即pre的后继是p); - 若
p的左指针为空,则将其ltag设为 1,并让lchild指向pre(即p的前驱是pre); - 更新
pre = p,继续遍历。
示例代码(中序线索化):
typedef struct ThreadNode { char data; struct ThreadNode *lchild, *rchild; int ltag, rtag; // 0: child, 1: thread } ThreadNode, *ThreadTree; ThreadNode *pre = NULL; // 指向前一个访问的节点 void inorderThreading(ThreadNode *p) { if (p == NULL) return; inorderThreading(p->lchild); // 遍历左子树 // 处理当前节点 if (p->lchild == NULL) { p->ltag = 1; p->lchild = pre; } if (pre != NULL && pre->rchild == NULL) { pre->rtag = 1; pre->rchild = p; } pre = p; inorderThreading(p->rchild); // 遍历右子树 } - 若
-
作用与优势:
- 提高遍历效率:无需栈或递归即可完成遍历;
- 节省空间:利用原本浪费的空指针域存储有用信息;
- 快速查找前驱/后继:在线索二叉树中可直接定位某节点的直接前驱或后继;
- 适用于频繁遍历但结构稳定的二叉树场景。
实现中序线索二叉树的非递归遍历,关键在于利用ltag和rtag标志位判断指针是指向孩子还是线索(前驱/后继),从而避免使用栈或递归。
基本思路:
- 从根节点开始,找到中序序列的第一个节点(即最左下的节点);
- 遍历过程中:
- 若当前节点的
rtag == 1,说明rchild指向的是中序后继,直接跳转; - 若
rtag == 0,说明有右孩子,则进入右子树,并找到该子树中序遍历的第一个节点(即右子树中最左边的节点);
- 若当前节点的
- 重复直到所有节点访问完毕。
C语言实现代码:
#include <stdio.h>
typedef struct ThreadNode {
char data;
struct ThreadNode *lchild, *rchild;
int ltag, rtag; // 0: child, 1: thread
} ThreadNode, *ThreadTree;
// 非递归中序遍历线索二叉树
void inorderTraverse(ThreadTree root) {
ThreadNode *p = root;
// 找到中序第一个节点:一直向左直到 ltag == 1
while (p != NULL && p->ltag == 0) {
p = p->lchild;
}
// 遍历整个线索二叉树
while (p != NULL) {
printf("%c ", p->data); // 访问当前节点
// 如果 rtag == 1,rchild 是后继线索,直接跳转
if (p->rtag == 1) {
p = p->rchild;
}
// 否则进入右子树,找右子树的最左节点
else {
p = p->rchild;
while (p != NULL && p->ltag == 0) {
p = p->lchild;
}
}
}
}
示例说明:
假设有一棵中序线索化的二叉树如下:
A
/ \
B C
/ \
D E
中序遍历序列:D → B → E → A → C
线索化后:
- D 的
rchild指向 B(后继),rtag = 1 - B 的
lchild指向 D(前驱),ltag = 1 - E 的
rchild指向 A(后继),rtag = 1 - A 的
lchild指向 E(前驱),ltag = 1 - C 的前后无更多节点,视情况处理
调用 inorderTraverse(root) 将输出:D B E A C
注意事项:
- 必须确保线索化已完成,否则行为未定义;
- 头节点可增设一个“头结点”统一管理起始与结束(如将最后一个节点的后继指向头结点,形成循环线索树);
- 时间复杂度:O(n),每个节点访问一次;
- 空间复杂度:O(1),无需额外栈空间。


1141

被折叠的 条评论
为什么被折叠?



