数据结构学习:利用链表建立二叉树

最初版本:利用链表保存二叉树
不足:浪费指针位置。

二叉树结构体

typedef char TElemType;
typedef struct BiTree{
    TElemType data;
    struct BiTree *Lchild,*Rchild;
}*BiTree,BiTNode;

每一个结点保存数据,左孩子和右孩子的数据。

创建二叉树
利用前序遍历法输入二叉树,没有的结点用#补全
注意传入的参数 是指向指针的指针 这样才能改变指针的值
c++的话也可以用引用代替

void Create_Bitree(BiTree *T){ //前序输入
    TElemType data;
    scanf("%c",&data);
    if(data == '#') T = NULL;
    else{
        if(!(*T = (BiTree)malloc(sizeof(BiTNode)))) exit(1);
        (*T)->data = data;
        Create_Bitree(&(*T)->Lchild);
        Create_Bitree(&(*T)->Rchild);
    }
}

前序遍历法遍历树

void Pre_Order(BiTree T){
    if(T!=NULL){
        printf("%c",T->data);
        Pre_Order(T->Lchild);
        Pre_Order(T->Rchild);
    }
}

中序遍历法遍历

void In_Order(BiTree T){
    if(T!=NULL){
        In_Order(T->Lchild);
        printf("%c",T->data);
        In_Order(T->Rchild);
    }
}

后序遍历法遍历

void Post_Order(BiTree T){
    if(T!=NULL){
        Post_Order(T->Lchild);
        Post_Order(T->Rchild);
        printf("%c",T->data);
    }
}

主函数

int main(){
    BiTree T;
    printf("请前序遍历输入树\n");
    Create_Bitree(&T);
    printf("前序输出这个二叉树为:\n");
    Pre_Order(T);
    printf("\n");
    printf("中序输出这个二叉树为:\n");
    In_Order(T);
    printf("\n");
    printf("后序输出这个二叉树为:\n");
    Post_Order(T);
    printf("\n");
    return 0;
}

进阶版本:(线索二叉树)
利用空指针位置储存前驱和后续;创造头节点,左孩子为树根,右孩子为中序最末结点(投影最后结点)。

结构体中加入Tag位保存是否该指针指向子结点。
若为Link则指向子结点,若为Thread则指向前驱或后续结点。
中序排序第一个(最左孩子)的左指针指向头结点,最后一个(最右孩子)的右指针指向头结点。

结构体创建如下

typedef char TElemType;
typedef enum { Link,Thread } PointerTag;


typedef struct BiTree{
    TElemType data;
    struct BiTree *Lchild,*Rchild;
    PointerTag LTag,RTag;
}*BiTree,BiTNode;

先来看看主函数
注意头结点由于是连接整个头尾的
所以如果在建立前序后继指针之前创建的话,就会进入死循环。

int main(){
    BiTree T,THead;
    printf("请前序遍历输入树\n");
    THead = (BiTree) malloc(sizeof(BiTNode));
    Create_Bitree(&T);//创建树
    pre = THead;
    In_Threading(T);//给树加上前驱和后续指针
    Create_Head(&THead,T);//创建头结点
    printf("中序输出树\n");
    In_Order_Traverse(THead);//中序遍历树
    printf("\n");
    return 0;
}

创建输入树时与原先一样

void Create_Bitree(BiTree *T){ //前序输入
    TElemType data;
    scanf("%c",&data);
    if(data == '#') T = NULL;
    else{
        if(!(*T = (BiTree)malloc(sizeof(BiTNode)))) exit(1);
        (*T)->data = data;
        Create_Bitree(&(*T)->Lchild);
        Create_Bitree(&(*T)->Rchild);
    }
}

令空余指针分别指向前驱和后继结点。
其中pre用来保存上一个结点的信息。

BiTree pre;
void In_Threading(BiTree T){
    if(T){
        In_Threading(T->Lchild);
        if(!T->Lchild){
            T->LTag = Thread;
            T->Lchild = pre;
        }
        if(!pre->Rchild){
            pre->RTag = Thread;
            pre->Rchild = T;
        }
        pre = T;
        In_Threading(T->Rchild);
    }
}

创建头结点

void Create_Head(BiTree *THead,BiTree T){
    (*THead)->LTag = Link;
    (*THead)->Lchild = T;//左孩子指向树根
    (*THead)->RTag = Thread;
    while(T->Rchild->Rchild) T = T->Rchild;
    T->Rchild->Rchild = (*THead); //最右结点的右孩子指向头结点
    T->Rchild->RTag = Thread;
    (*THead)->Rchild = T; //头结点的右孩子指向最右结点
}

遍历函数

void In_Order_Traverse(BiTree T){
    BiTree P = T->Lchild;
    while(P!=T){
        while(P->LTag == Link){
            P = P->Lchild;
        }//到达最左孩子
        printf("%c",P->data);
        while(P->RTag == Thread &&P->Rchild !=T){
            P = P->Rchild;//后继指针开始起作用了 一路遍历下去。
            printf("%c",P->data);
        }//该结点的左子树遍历完毕
        P = P->Rchild;//开始该结点的的右子树
    }
}



举个例子 各结点保存如下

这里写图片描述

线索二叉树相较于普通二叉树,其优势在于能更高效的保存信息。 在树中任意一个叶子结点,都可以知道其前驱和后继结点,而不占更多的储存空间。

最后附上完整代码

#include <stdio.h>
#include <stdlib.h>


typedef char TElemType;
typedef enum { Link,Thread } PointerTag;


typedef struct BiTree{
    TElemType data;
    struct BiTree *Lchild,*Rchild;
    PointerTag LTag,RTag;
}*BiTree,BiTNode;

void Create_Bitree(BiTree *T){ //前序输入
    TElemType data;
    scanf("%c",&data);
    if(data == '#') T = NULL;
    else{
        if(!(*T = (BiTree)malloc(sizeof(BiTNode)))) exit(1);
        (*T)->data = data;
        Create_Bitree(&(*T)->Lchild);
        Create_Bitree(&(*T)->Rchild);
    }
}


BiTree pre;

void In_Threading(BiTree T){
    if(T){
        In_Threading(T->Lchild);
        if(!T->Lchild){
            T->LTag = Thread;
            T->Lchild = pre;
        }
        if(!pre->Rchild){
            pre->RTag = Thread;
            pre->Rchild = T;
        }
        pre = T;
        In_Threading(T->Rchild);
    }
}

void In_Order_Traverse(BiTree T){
    BiTree P = T->Lchild;
    while(P!=T){
        while(P->LTag == Link){
            P = P->Lchild;
        }
        printf("%c",P->data);
        while(P->RTag == Thread &&P->Rchild !=T){
            P = P->Rchild;
            printf("%c",P->data);
        }
        P = P->Rchild;
    }
}

void Create_Head(BiTree *THead,BiTree T){
    (*THead)->LTag = Link;
    (*THead)->Lchild = T;
    (*THead)->RTag = Thread;
    while(T->Rchild->Rchild) T = T->Rchild;
    T->Rchild->Rchild = (*THead);
    T->Rchild->RTag = Thread;
    (*THead)->Rchild = T;
}

int main(){
    BiTree T,THead;
    printf("请前序遍历输入树\n");
    THead = (BiTree) malloc(sizeof(BiTNode));
    Create_Bitree(&T);
    pre = THead;
    In_Threading(T);
    Create_Head(&THead,T);
    printf("后序输出树\n");
    In_Order_Traverse(THead);
    printf("\n");
    return 0;
}

转载于:https://www.cnblogs.com/he11o-liu/p/7503267.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值