线索二叉树
二叉树线索化
⚠️ 被线索化之后的lchild
与rchild
表示前驱与后继
// 中序遍历对二叉树线索化的递归算法
void InThread(ThreadTree &p, ThreadTree &pre) {
if (p != NULL) {
InThread(p->lchild, pre); // 一直递归到最左子树
/* 中序遍历 */
if(p->lchild == NULL) { // 没有左孩子就指向前驱
p->lchild = pre; // 建立当前节点的前驱线索
p->ltag = 1;
}
if(pre!=NULL&&pre->rchild==NULL){
pre->rchild = p; // 建立前驱节点的后继线索
pre->rtag = 1;
}
pre = p;
/* 中序遍历 */
InThread(p->rchild, pre); // 递归线索化右子树
}
}
// 中序遍历建立中序线索二叉树
void CreateInThread(ThreadTree T) {
ThreadTree pre = NULL;
if (T != NULL) {
pre->rchild = NULL;
pre->rtag = 1;
}
}
算法的思想很简单:算法整体思路与中序遍历一致,按照“左根右”的顺序编排代码。
- 首先InThread一直递归到最左子树;
- 其次我们要帮助p节点寻找前驱节点,建立前驱线索;
- 之后我们要帮助pre节点寻找后继节点,建立后继线索;
- 最后InThread向右递归;
总结:找到某个状态,完整状态的转变,最后递归解决问题。
附上先序代码与后序代码
// 先序线索化
void PreThread(ThreadTree &p, ThreadTree &pre) {
if (p != NULL) {
if (p->lchild) { // 左子树为空,建立前驱线索
p->lchild = pre;
p->ltag = 1;
}
if (pre!=NULL && pre->rchild==NULL) { // 建立前驱节点的后继线索
pre->rchild = p;
pre->rtag = 1;
}
pre = p;
if (p->ltag == 0) // 关键点,防止爱滴魔力转圈圈
PreThread(p->lchild, pre);
PreThread(p->rchild, pre);
}
}
void CreatePreThread(ThreadTree T) {
ThreadTree pre = NULL;
if (T != NULL) {
PreThread(T, pre);
if(pre->rchild == NULL)
pre->rtag = 1;
}
}
// 后序线索化
void PostThread(ThreadTree p, ThreadTree &pre) {
if(p!=NULL) { // 递归结束条件
PostThread(p->lchild, pre);
PostThread(p->rchild, pre);
if(p->lchild=NULL) { // 左子树为空,建立前驱线索
p->lchild = pre;
p->ltag = 1;
}
if(pre!=NULL&&pre->rchild==NULL){ // 建立前驱节点的后继线索
pre->rchild = p;
pre->rtag = 1;
}
pre = p;
}
}
// 后序线索化二叉树
void CreatePostThread(ThreadTree T) {
ThreadTree pre = NULL;
if(T!=NULL) {
PostThread(T, pre);
if(pre->rchild==NULL) {
pre->rtag = 1;
}
}
}
线索化二叉树寻找前驱/后继
对于中序线索二叉树中寻找指定节点*p的中序后继next
- p->rtag == 1, next = p->rchild
- p->rtag == 0, p右子树中最左下节点
ThreadNode *Firstnode(ThreadNode *p) {
// 有左子树就一直往左走
while(p->ltag == 0) {
p = p->lchild;
}
return p;
}
ThreadNode *NextNode(ThreadNode *p) {
// 如果rtag==0,就从右子树中找到最左下的节点
if(p->rtag == 0) return Firstnode(p->rchild);
else return p->rchild; // rtag==1 则直接返回后继
}
// 中序线索化二叉树进行中序遍历(非递归)
void Inorder(ThreadNode *T) {
for(ThreadNode *p = Firstnode(T); p!=NULL; p=Nextnode(p)) {
visit(p);
}
}
对于中序线索二叉树中寻找指定节点*p的中序前驱pre
- p->ltag == 1, pre = p->lchild
- p->rltag == 0, p左子树中最右下节点
ThreadNode *Lastnode(ThreadNode *p) {
while(p->rtag==0) { // 有右子树就一直往右走
p = p->rchild;
}
return p;
}
ThreadNode *Prenode(ThreadNode *p) {
// 寻找左子树最右下节点
if(p->ltag==0) return Lastnode(p->lchild);
else return p->lchild;
}
// 对中序线索二叉树进行逆向中序遍历
void RevInorder(ThreadNode *T) {
for(ThreadNode *p=Lastnode(T); p!=NULL; p=Prenode(p))
visit(p);
}
对于先序线索二叉树中寻找指定节点*p的先序后继next
- p->rtag == 1, pre = p->lchild
- p->rtag == 0, p是左孩子或右孩子的根节点
对于先序线索二叉树中寻找指定节点*p的先序前驱pre
- p->ltag == 1, pre = p->lchild
- p->ltag == 0, 土办法或者改用三叉链表
-
- 如果能找到p的父节点且p是左孩子,p的父节点即为pre
- 如果能找到p的父节点且p是右孩子,p的兄弟节点为空,p的父节点即为pre
- 如果能找到p的父节点且p是右孩子,p的左兄弟节点不为空,p的左兄弟节点的最右下节点即为pre
- p为根节点,无前驱
-
对于后序线索二叉树寻找指定节点*p的后序后继next
- p->ltag == 1, pre = p->lchild
- p->ltag == 0, 土办法或者改用三叉链表
-
- 如果能找到p的父节点且p是右孩子,p的父节点即为next
- 如果能找到p的父节点且p是左孩子,p的兄弟节点为空,p的父节点即为next
- 如果能找到p的父节点且p是左孩子,p的右兄弟节点不为空,p的右兄弟节点的最左下节点即为next
- p为根节点,无后继
-
相关考点:
- 后序线索树的遍历仍然需要栈的支持
分析:普通的二叉树不管是递归算法(隐含递归栈),还是非递归算法都需要栈的支持。先序和中序线索二叉树由于线索化,不需要栈(因为能找到下一个遍历对象)。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具