数据结构与算法(11)- 线索化二叉树

357 阅读3分钟

在二叉树的结点上加上线索二叉树称为线索二叉树,对二叉树以某种遍历方式(先序,中序,后序)进行遍历,使其变为线索二叉树的过程称为对二叉树进行线索化。

概念

对于 n 个结点的二叉树,在二叉树存储结构中有 n + 1 个空链域,利用这些空链域放在某种遍历次序下该结点的前驱点和后继点的指针,这些指针称为线索,加上线索的二叉树称为线索二叉树

作用

  • 利用被浪费的 n + 1 个空链域,提高利用率
  • 存放在某种遍历次序下该结点的前驱结点或后继结点的指针,保存前驱或后继结点的引用,利用线索二叉树进行中序遍历时,不必采用堆栈处理,速度较一般二叉树的遍历速度快,且节约存储空间。
  • 任意一个结点都能直接找到它的前驱和后继结点

缺点

  • 结点的插入和删除麻烦,且速度也较慢
  • 线索子树不能共用

结点结构

带有标识位的线索二叉树

代码实现

typedef int ret;
typedef char Element;
Element Nil = '#';

//1、定义结点结构

//Link = 0,表示指向左右孩子指针
//Thread = 1,表示指向前后前驱或后继的线索
typedef enum {Link, Thread} PointTag;

typedef struct BiTreeNode {
    //数据
    Element data;
    //左右孩子结点指针
    struct BiTreeNode *lchild, *rchild;
    
    //左右标记
    PointTag Ltag;
    PointTag Rtag;
}biTreeNode, *BiTreNode;


//2、将 chars 字符串存入 T
int indexs = 1;
typedef char String[24];
String str;
ret strAssign(String T, char *chars) {
    T[0] = strlen(chars);
    if (T[0] <= 0 || T[0] > MAXSIZE) {
        return ERROR;
    } else {
        for (int i = 1; i <= T[0]; i++) {
            T[i] = chars[i-1];
        }
    }
    
    return OK;
}

//3、打印
ret visit(Element e) {
    printf("%c ", e);
    return OK;
}

//4、构造二叉树
ret creatBiTree(BiTreNode *T) {
    Element h;
       //scanf("%c",&h);
       //获取字符
       h = str[indexs++];
       
    if (h == Nil) {
        *T = NULL;
    } else {
       *T = (BiTreNode)malloc(sizeof(BiTreNode));
        if (!*T) {
            exit(OVERFLOW);
        }
        //生成根结点(前序)
        (*T)->data = h;
        
        //递归构造左子树
        creatBiTree(&(*T)->lchild);
        //存在左孩子->将标记LTag设置为Link
        if ((*T)->lchild) (*T)->Ltag = Link;
        
        //递归构造右子树
        creatBiTree(&(*T)->rchild);
        //存在右孩子->将标记RTag设置为Link
        if ((*T)->rchild) (*T)->Rtag = Link;
    }
    return OK;
}

//5、中序遍历二叉树T,将其中序线索化,thrt 指向头结点

BiTreNode pre; //全局变量,始终指向刚刚访问过的结点

//中序遍历进行中序线索化
void inThreading(BiTreNode p) {
    if (p) {
        inThreading(p->lchild);
        //无左孩子
        if (!p->lchild) {
            //前驱线索
            p->Ltag = Thread;
            //左孩子指针指向前驱
            p->lchild  = pre;
        }else
        {
            p->Ltag = Link;
        }
        
       //前驱没有右孩子
        if (!pre->rchild) {
            //后继线索
            pre->Rtag = Thread;
            //前驱右孩子指针指向后继(当前结点p)
            pre->rchild = p;
        }else
        {
            pre->Rtag = Link;
        }
        
        //保持pre指向p的前驱
        pre = p;
        //递归右子树线索化
        inThreading(p->rchild);
    }
}

//6、中序遍历二叉树 T,并将其中序线索化,thrt 指向头结点
ret inOrderThreading(BiTreNode *thrt, BiTreNode T) {
     *thrt=(BiTreNode)malloc(sizeof(BiTreNode));
       
       if (! *thrt) {
           exit(OVERFLOW);
       }
       
       //建立头结点;
       (*thrt)->Ltag = Link;
       (*thrt)->Rtag = Thread;
       //右指针回指向
       (*thrt)->rchild = (*thrt);
    
    if (!T) {
        (*thrt)->lchild=*thrt;
    }else{
        
        (*thrt)->lchild=T;
        pre=(*thrt);
        
        //中序遍历进行中序线索化
        inThreading(T);
        
        //最后一个结点rchil 孩子
        pre->rchild = *thrt;
        //最后一个结点线索化
        pre->Rtag = Thread;
        (*thrt)->rchild = pre;
        
    }
    
     /* 若二叉树空,则左指针回指 */
    return OK;
}

//7、中序遍历二叉线索数T
ret inOrderTraverse_Thr(BiTreNode T) {
    BiTreNode p;
   p=T->lchild; /* p指向根结点 */
    while(p!=T)
    { /* 空树或遍历结束时,p==T */
        while(p->Ltag==Link)
            p=p->lchild;
        if(!visit(p->data)) /* 访问其左子树为空的结点 */
            return ERROR;
        while(p->Rtag==Thread&&p->rchild!=T)
        {
            p=p->rchild;
            visit(p->data); /* 访问后继结点 */
        }
        p=p->rchild;
    }
    
    return OK;
}


int main(int argc, const char * argv[]) {
    // insert code here...
    printf("Hello, 线索化二叉树!\n");
    BiTreNode H,T;
    strAssign(str,"ABDH##I##EJ###CF##G##");
    creatBiTree(&T); /* 按前序产生二叉树 */
    inOrderThreading(&H,T); /* 中序遍历,并中序线索化二叉树 */
    inOrderTraverse_Thr(H);
    printf("\n\n");
       
    return 0;
}

打印结果: