寻找线索二叉树的前驱后继,以及遍历线索二叉树

中序线索二叉树寻找前驱后继及遍历

定义线索二叉树:

typedef struct ThreadNode
{
    int data;
    struct ThreadNode *lchild, *rchild;
    int ltag, rtag;
} ThreadNode, *ThreadTree;

寻找中序序列的后继结点及正向遍历

中序遍历顺序是:根节点、左子树、右子树
如:根结点、左子树p、右子树——>
根节点、(左子树、根节点p、右子树)、右子树
所以寻找后继结点只需要判断rtag的值,rtag==0时,找左子树的最左边结点即可。
①若rtag = = 1,则后继=p->rchild
②若rtag = = 0,则后继=p右孩子中序遍历的第一个结点
也就是右子树最左边的那个结点。

//找到以p为根结点的子树中,第一个被中序遍历的结点
ThreadNode *Firstnode(ThreadNode *p)
{
    while (p->ltag == 0)
    {
        //循环找到最左下角的结点,不一定是叶节点,只是没有左孩子
        p = p->lchild;
    }
    return p;
}

//在中序线索二叉树中,找到p的后继结点,因为中序遍历是左子树、根结点、右子树
//所以只需要判断rtag的值,若为1,则直接返回右孩子,不为1,则寻找右子树的第一个被中序遍历的点
ThreadNode *NextNode(ThreadNode *p)
{
    if (p->rtag == 1)
    {
        return p->rchild;
    }
    else
    {
        return Firstnode(p->rchild);
    }
}

void visit(ThreadNode *p)
{
    //自定义操作
}

//找到后继结点之后,就可以方便的开始对中序序列进行遍历了
void Inorder(ThreadNode *T)
{
    for (ThreadNode *p = Firstnode(T); p != nullptr; p = NextNode(p))
    {
        visit(p); //visit()访问函数
    }
}

寻找中序遍历的前驱结点及逆向遍历

①若ltag = = 1,则前驱=p->lchild
②若ltag = = 0,则前驱=p左孩子中序遍历的最后一个结点
也就是左子树最右边那个结点

ThreadNode *Fright_Node(ThreadNode *p)
{
    while (p->rtag == 0)
    {
        p = p->rchild;
    }
    return p;
}

ThreadNode *PreNode(ThreadNode *p)
{
    if (p->ltag==0)
    {
        return Fright_Node(p->lchild);
    }
    else return p->lchild;
}

//对中序线索二叉树进行逆向的中序遍历
void RevInorder(ThreadNode *T)
{
    for(ThreadNode *p=Fright_Node(T);p!=nullptr;p=PreNode(p))
    {
        visit(p);
    }
}

先序线索二叉树寻找后继结点及前驱结点

寻找先序序列的后继结点

先序遍历依次为:根结点、左子树p、右子树——>
根结点、(根结点p、左子树、右子树)、右子树
所以寻找先序序列的后继结点,只需要看根结点的左右孩子便可。
①若rtag = = 1,则后继=p->rchild
②若rtag = = 0,则后继=p左孩子先序遍历的第一个结点

ThreadNode *Pre_NextNode(ThreadNode *p)
{
    //判断结点rtag的值,若==1,则存在后继,
    //(包含左右孩子不存在的情况,p->lchild==NULL)
    if (p->rtag==1)
    {
        return p->lchild;
    }

    //若结点有左孩子,则返回左孩子根结点
    //p->rtag==0&&p->lchild
    else if(p->lchild)
    {
        return p->lchild;
    }

    //若结点没有左孩子,有右孩子,则返回右孩子根节点
    //(p->rtag==0&&p->lchild==nullptr&&p->rchild)
    else
    {
        return p->rchild;
    }
}

寻找先序序列的前驱结点

①若ltag = = 1,则前驱=p->lchild
②若ltag = = 0,则结点p遍历顺序为:根p、左、右
可以看出,是没有办法用p表示他的前驱结点的,所以必须从头开始遍历,寻找他的前驱结点,具体做法见另一篇文章:https://blog.csdn.net/qq_41185288/article/details/107183176

除非设置三叉链表,多一个指针指向它的父节点。
如果存在一个指针指向它的父节点:

一、p是左孩子

先序遍历序列:根、左、右——>根、(根p、左、右)、右
故p的前驱即为其父节点

二、p是右孩子且左孩子不存在

先序遍历序列:根、右——>根、(根p、左、右)
p的父结点即为其前驱结点

三、p是右孩子,且左孩子存在

先序遍历序列:根、左、右——>
根、(根、左、右)、(根p、左、右)
故p的前驱结点即为父节点的左孩子的最后一个访问的结点。

较复杂的访问情况:
在这里插入图片描述
按根、左、右依次访问下去,找到最后一个结点即可。

后序线索二叉树寻找后继结点及前驱结点

寻找后序序列的后继结点

后序遍历次序为:左、右、根

①若rtag = = 1,则后继=p->rchild
②若rtag = = 0,则结点p一定有右孩子:左、右、根p
可以看出,p的左右孩子都不是p的后继,所以只能从头开始暴力遍历寻找根p的后继。

也可以设置三叉链表,多一个指针指向父节点
如果存在一个指针指向父节点,则:

一、p是右孩子

后序遍历序列:左、右p、根——>左、(左、右、根p)、根
结点p的后继结点即为p的父节点

二、p是左孩子且父节点没有右孩子

后序遍历序列:左p、根——>(左、右、根p)、根
结点p的后继结点即为p的父节点

三、p是左孩子且父节点有右孩子

后序遍历序列:左p、右、根——>(左、右、根p)、右、根
结点p的后继即为右兄弟子树后序遍历访问的第一个结点

四、p是根结点

后序遍历序列:左、右、根p
可以看出p没有后继结点

寻找后序序列的前驱结点

后序遍历次序为:左、右p、根——>
左、(左、右、根p)、根

①若ltag = = 1,则前驱=p->lchild
②若ltag = = 0,则前驱=p的右孩子或者左孩子(当右孩子不存在时)

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值