线索二叉树的简单介绍
线索二叉树就是利用空闲指针来连接结点的前驱结点和后继结点的二叉树。引入线索二叉树的目的是加快查找前驱结点和后继结点的速度。
(遍历二叉树会产生一个序列,在这个序列中,除了第一个和最后一个结点,其他结点的左右都有结点。把某结点 x 相邻的前面的那个结点就叫做它的前驱结点,相邻的后面的那个结点就叫做它的后继结点)
在含有 n 个结点的二叉树中一定有且仅有 n+1 个空指针。利用这些空闲的指针,使它们指向前驱结点和后继结点,就能像链表那样遍历整个线索二叉树。
线索二叉树的结点定义
struct ThreadNode {
ElemType val; //值
ThreadNode *lchild, rchild; //左右孩子
int ltag, rtag; //左右线索标志,0表示右孩子,1表示有线索
};
中序线索二叉树的构造
思路是:在中序遍历的过程中,把空闲指针指向前驱和后继结点
void inThread(ThreadTree &p, ThreadTree &pre) {
if (p == nullptr) return nullptr;
//构造中序线索二叉树
inThread(p->lchild, pre);
//如果p的左孩子为空,令p的左孩子指向p的前驱结点pre
if (p->lchild == nullptr) {
p->lchild = pre;
p->ltag = 1;
}
//如果p的前驱结点pre的右孩子为空,令pre的右孩子指向pre的后继结点p
if (pre != nullptr && p->rchild == nullptr) {
pre->rchild = p;
pre->rtag = 1;
}
pre = p;
inThread(p->rchild, pre);
return;
}
void createInThread(ThreadTree p) {
ThreadTree pre == nullptr;
if (t == nullptr) return;
inThread(p, pre);
//序列的最后一个结点没有后继结点
pre->rchild = nullptr;
pre->rtag = 1;
}
遍历中序线索二叉树
像链表那样遍历,所以要先得到序列中的第一个结点,然后再获得它的后继结点,依次遍历下去。
(后序线索树的遍历还是需要栈的支持)
//获取中序序列中的第一个结点,即最左下角的那个结点
ThreadNode* firstNode(ThreadNode *p) {
//从根节点出发,当结点有左孩子时,继续向下走
while (p->ltag == 0) p = p->lchild; //或者 while (p->lchild != nullptr)
return p;
}
//获取中序序列中某结点的后继结点
ThreadNode* nextNode(ThreadNode *p) {
//如果有右孩子,返回右子树中序遍历序列中的第一个结点,即右子树的最左下结点
if (p->rtag == 0) return firstNode(p->rchild);
//没有右孩子,就直接返回右线索
return p->rchild;
}
void inorder(ThreadNode *p) {
for (ThreadNode *t = firstNode(p); t != nullptr; t = nextNode(t)) {
visit(t);
}
}
在线索二叉树中,有的结点可以直接找到它的前驱结点和后继结点,有的节点就比较麻烦
线索二叉树的查找
先序线索树
查找先序前驱:比较复杂,需要知道父结点
查找先序后继:若有左孩子,就是左孩子;无左孩子,若有右孩子,就是右孩子,否则就是右线索
中序线索树
查找中序前驱:若有左孩子,就是左子树的最右节点,否则就是左线索
查找中序后继:若有右孩子,就是右子树的最左结点,否则就是右线索
后序线索树
查找后序前驱:若有右孩子,就是右孩子;无右孩子,若有左孩子,就是左孩子,否则就是左线索
查找后序后继:比较复杂,需要知道父节点
例题
例:在中序线索树中查找后序序列中某结点的前驱结点
思路:在后序线索树中查找某结点 p 的后序前驱结点:若 p 有右孩子,就是右孩子(若 p 有右孩子,那么在后序序列中,右孩子一定在 p 的前一位);无右孩子,若有左孩子,就是左孩子,否则就是左线索;
根据题意改成:若有右孩子,就是右孩子;无右孩子,若有左孩子,就是左孩子;如果左右孩子都没有,向上找第一个有左孩子的祖先节点,左孩子即为前驱结点
ThreadNode* inTreefindPostPreNode(ThreadNode *t, ThreadNode *p) {
if (p->rtag == 0) return p->rchild;
else if (p->ltag == 0) return p->lchild;
else if (p->lchild == nullptr) return nullptr;
else {
while (p->ltag == 1 && p->lchild != nullptr) {
p = p->ltag;
}
if (p->ltag == 0) return p->lchild;
else return nullptr;
}
return nullptr;
}