线索二叉树

 

由图可以看出,普通的二叉树存储3个结点的值会浪费4个指针指向空,这不仅浪费空间而且还浪费时间;而线索二叉树就可有效的补足这个缺点

线索二叉树利用中序遍历刚好使造成浪费的结点均处于字符中间,可以很好的利用 "浪费掉的空指针" 来存放前驱和后继的指针,组成循环链表;所以在原先结点结构上新加两个指向中序的前驱和后继,就可以有效的利用造成浪费的结点

#include <stdio.h>
#include <malloc.h>
typedef char ElemType;

//标志位:
//child:指向 左孩子/右孩子
//thread:指向 前驱/后继
typedef enum{child,thread}Tag;

typedef struct BirTNode{
    ElemType data;
    struct BirTNode *lchild;//左孩子
    struct BirTNode *rchild;//右孩子
    Tag ltag;//左边标志
    Tag rtag;//右边标志
}BirTNode,* BirTree;

BirTree pro;

void creat(BirTree tree,ElemType val){
    ElemType v;
    BirTree l,r;
    tree->data=val;
    printf("输入%c左子树的根值(以空格结束):\n",tree->data);
    v=getch();
    if(' '!=v){
        l=(BirTree)malloc(sizeof(BirTNode));
        tree->lchild=l;
        tree->ltag=child;
        creat(l,v);
    }else{
        tree->ltag=child;
        tree->lchild=NULL;
    }
    printf("输入%c右子树的根值(以空格结束):\n",tree->data);
    v=getch();
    if(' '!=v){
        r=(BirTree)malloc(sizeof(BirTNode));
        tree->rchild=r;
        tree->rtag=child;
        creat(r,v);
    }else{
        tree->rtag=child;
        tree->rchild=NULL;
    }
}

void Thread(BirTree tree){
    if(tree){
        Thread(tree->lchild);
        if(!tree->lchild){
            tree->ltag=thread;
            tree->lchild=pro;
        }
        if(!pro->rchild){
            pro->rtag=thread;
            pro->rchild=tree;
        }
        pro=tree;
        Thread(tree->rchild);
    }
}

void Inthread(BirTree *p,BirTree tree){
    *p=(BirTree)malloc(sizeof(BirTNode));
    (*p)->ltag=child;
    (*p)->rtag=thread;
    (*p)->rchild=*p;
    if(!tree){
        (*p)->lchild=*p;
    }else{
        (*p)->lchild=tree;
        pro=*p;
        Thread(tree);
        pro->rtag=thread;
        pro->rchild=*p;
        (*p)->rchild=pro;
    }
}

void visit(ElemType e){//操作结点
    printf("%c  ",e);
}

void cenprint(BirTree p){//p: 相当于循环链表的头结点,没有数据
    BirTree tree=p->lchild;//tree: 树的根节点
    //由于创建时树是中序插入
    //若前序最后一个结点正好是中序第一个结点,那么这个结点的前驱和后继都是头结点
    //也正由于第一个循环避免了递归
    while(p!=tree){
        //找到树的中序第一个结点
        while(tree->ltag==child){
            tree=tree->lchild;
        }
        visit(tree->data);
        //根据向后线索遍历,直到遍历到右边是子树停止,回溯到第一个循环遍历右子树
        //根据线索向后遍历,遍历到头结点
        while(tree->rtag==thread && tree!=pro){
            tree=tree->rchild;
            visit(tree->data);
        }
        tree=tree->rchild;
    }
    printf("\n");
}

void main(){
    ElemType val;
    BirTree p,tree=(BirTree)malloc(sizeof(BirTNode));
    printf("请输入根节点的值(空格结束):\n");
    val=getch();
    creat(tree,val);

    Inthread(&p,tree);

    printf("中序遍历树:\n");
    cenprint(p);
}