温故知新:中序线索二叉树(thread binary tree)

本文介绍了中序线索二叉树的概念,通过设置左右孩子线索来指向前驱和后继节点,使得在非空指针时能快速访问。文章详细阐述了线索化过程,从根节点开始,利用pre指针记录前驱节点,并在后序遍历中销毁节点。还提供了非递归的前序和中序遍历线索二叉树的方法,简化了遍历操作。
摘要由CSDN通过智能技术生成
  线索二叉树是在二叉树的基础上改进得到的,在原有的二叉树结点中添加两个变量分别用来记录该结点的左右孩子是否线索化。
struct threadBinaryTreeNode
{
    elementType number;
    dataType data;
    threadBinaryTreeNode<elementType,dataType> *lchild,*rchild;
    int leftThread,rightThread;
    threadBinaryTreeNode():lchild(0),rchild(0),leftThread(0),rightThread(0){}
    threadBinaryTreeNode(elementType a,dataType b):number(a),data(b),lchild(0),rchild(0),leftThread(0),rightThread(0){}
};

我们这样规定,左孩子线索化后为指向该结点的前驱结点,右孩子线索化后为指向该结点的后继结点。那么,就有了,对于拥有空指针的二叉树结点,我们将其线索化使得空指针指向该结点的前驱或后继。

下面给出一个类thread binary tree

template<typename elementType,typename dataType>
class threadBinaryTree
{
private:
    threadBinaryTreeNode<elementType,dataType> *root;
    threadBinaryTreeNode<elementType,dataType> *headNode;
    threadBinaryTreeNode<elementType,dataType> *pre;
    int n;
public:
    threadBinaryTree();
    ~threadBinaryTree();
    void insertNode(const elementType &number,const dataType &data);
    void createBT();
    void threadBinaryTree_inorder(threadBinaryTreeNode<elementType,dataType> *const tn);
    void threadBinaryTree_inorder();
    void preorder();
    void inorder();
    //void postorder();
};

其中,root指向根结点,headNode为一个头结点,pre被用来保存当前结点的双亲结点,n是记录线索二叉树一共有多少结点。

构造函数和析构函数

//构造函数
template<typename elementType,typename dataType>
threadBinaryTree<elementType,dataType>::threadBinaryTree():root(0),pre(0),n(0)
{
    headNode=new threadBinaryTreeNode<elementType,dataType>[1];
    headNode->lchild=0;
    headNode->rchild=0;
    headNode->leftThread=0;
    headNode->rightThread=0;
}
//析构函数
template<typename elementType,typename dataType>
threadBinaryTree<elementType,dataType>::~threadBinaryTree()
{
    struct sta{threadBinaryTreeNode<elementType,dataType> *node;int flag;};
    sta s[n];
    int top=-1;
    threadBinaryTreeNode<elementType,dataType> *p=root;
    while(top>-1 || p==root)
    {
        while(p!=0)
        {
            top++;
            s[top].node=p;
            s[top].flag=0;
            p=p->lchild;
        }
        while(top>-1 && s[top].flag==1)
            delete s[top--].node;
        if(top>-1)
        {
            s[top].flag=1;
            p=s[top].node->rchild;
        }
    }
    delete headNode;
}

析构函数用非递归的后序遍历来删除树的结点。

下面开始线索化二叉树,这儿用中序线索化。

首先,要构造一颗二叉树

//插入函数
template<typename elementType,typename dataType>
void threadBinaryTree<elementType,dataType>::insertNode(const elementType &number,const dataType &data)
{
    threadBinaryTreeNode<elementType,dataType> *p;
    p=new threadBinaryTreeNode<elementType,dataType>[1];
    p->data=data;
    p->number=number;
    p->lchild=0;
    p->rchild=0;
    p->leftThread=0;
    p->rightThread=0;
    if(root==0)
        root=p;
    else
    {
        pre=root;
        threadBinaryTreeNode<elementType,dataType> *q=root;
        while(q)
        {
            pre=q;
            if(p->data>q->data)
                q=q->rchild;
            else
                q=q->lchild;
        }
        if(p->data>pre->data)
            pre->rchild=p;
        else
            pre->lchild=p;
    }
}
//创建二叉树
template<typename elementType,typename dataType>
void threadBinaryTree<elementType,dataType>::createBT()
{
    cout<<"输入二叉树的结点个数: ";
    cin>>n;
    cout<<"输入结点编号和信息,如 number data. \n";
    for(int i=0;i<n;i++)
    {
        elementType number;
        dataType data;
        cin>>number>>data;
        insertNode(number,data);
    }
    cout<<"\n----------二叉树创建完成...\n";
}
有了二叉树就可以对其中序线索化,
template<typename elementType,typename dataType>
void threadBinaryTree<elementType,dataType>::threadBinaryTree_inorder(threadBinaryTreeNode<elementType,dataType> *const p)
{
    if(p->lchild)
    {
        p->leftThread=0;
        threadBinaryTree_inorder(p->lchild);
    }
    else
    {
        p->lchild=pre;
        p->leftThread=1;
    }
    if(pre->rchild)
        pre->rightThread=0;
    else
    {
        pre->rchild=p;
        pre->rightThread=1;
    }
    pre=p;
    if(p->rchild) threadBinaryTree_inorder(p->rchild);
}
template<typename elementType,typename dataType>
void threadBinaryTree<elementType,dataType>::threadBinaryTree_inorder()
{
    if(root==0) return;
    cout<<"\n----------中序线索化...\n";
    headNode=new threadBinaryTreeNode<elementType,dataType> [1];//头结点
    headNode->lchild=root;
    headNode->rchild=0;
    headNode->leftThread=0;
    headNode->rightThread=0;
    pre=headNode;
    threadBinaryTree_inorder(root);
    headNode->rchild=pre;
    headNode->rightThread=1;
    pre->rchild=headNode;
    pre->rightThread=1;
    cout<<"\n----------中序线索二叉树创建完成...\n";
}

先看第一个函数,使用到了数据成员pre,在线索化的过程中,pre所起的作用等同于教材中的全局变量,其作用是用来记录当前结点的前驱结点。线索化的思路是,从根结点开始,若左孩子非空则对其进行递归调用,若左孩子为空,则使其指向当前结点的前驱pre;判断完左孩子,接着看pre指针,由于pre为前驱结点,那么判断pre所指结点的右孩子是否为空,若为空,则令右孩子指针指向当前结点;最后再对右孩子递归;

可以看到,中序线索化的大致方法就是,对当前结点判断是否有前驱,对前驱结点判断是否有后继。需要说明的是,这儿pre指针初始化时时指向headNode结点(见第二个函数),中序线索化后,headNode结点的左孩子指向root结点,右孩子指向中序遍历的最后一个结点。这样,就可以从第一个结点开始沿着后继结点开始遍历,或从最后一个结点开始沿着前驱开始遍历。

下面是非递归的前序和中序遍历线索二叉树,和二叉树相比简短很多

template<typename elementType,typename dataType>
void threadBinaryTree<elementType,dataType>::preorder()
{
    pre=root;
    while(pre!=headNode)
    {
        while(pre->leftThread==0)
        {
            cout<<pre->number<<" ";
            pre=pre->lchild;
        }
        cout<<pre->number<<" ";
        while(pre->rightThread==1 && pre->rchild!=headNode)
            pre=pre->rchild;
        pre=pre->rchild;
    }
}
template<typename elementType,typename dataType>
void threadBinaryTree<elementType,dataType>::inorder()
{
    pre=root;
    while(pre!=headNode)
    {
        while(pre->leftThread==0)
            pre=pre->lchild;
        cout<<pre->number<<" ";
        while(pre->rightThread==1 && pre->rchild!=headNode)
        {
            pre=pre->rchild;
            cout<<pre->number<<" ";
        }
        pre=pre->rchild;
    }
}
线索化二叉树后,寻找某结点的前驱和后继十分便利,以后许多博文进行了说明,这儿就不提了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值