显示二叉树(C语言实现)

**问题描述:**

函数DisplayTree()输出二叉树的直观示意图。传递给DisplayTree()的参数有

树根、所有数据值的最大宽度dataWidth以及屏幕的宽度screenWidth。

**基本要求及提示:**

宽度参数使我们能对屏幕输出进行安排。

假设dataWidth是2。而screenWidth为64=2的6次幂。

宽度为2的幂使得我们可以逐层对数据的组织进行描述。

因为不能确定树的结构,所以假定屏幕空间可以容纳完全二叉树。

假设结点实在坐标(level,indentedSpace)处画出。

第1层:根在(1,32)处画出。

第2层:因为根结点缩进32个空格,所以下一层的偏移量(offset)为32/2=16= screenWidth/2的2次幂。

第2层的两个结点的位置分别为(2,32- offset)和(2,32+offset)。

即(2,16)和(2,48)。

第3层:第3层的偏移量(offset)为screenWidth/2的3次幂。

第3层的4个结点的位置分别为(3,16- offset)、(3,16+offset)、(3,48- offset)和(3,48+offset)。

即(3,8)、(3,24)、(3,40)和(3,56)。

第i层的偏移量(offset)为screenWidth/2的i次幂

第i层的每个结点的位置是访问第i-1层其双亲结点时确定的。假设其双亲位置是(i-1,parentPos)。

若其第i层的结点是左孩子,那么它的位置是(i,parentPos-offset);若是右孩子则位置为(i,parentPos+offset)。

DisplayTree()函数用两个队列和按层遍历树中的结点。

队列Q中存放结点,二队列QI中则以记录类型Info存放结点的层次和打印位置。

当结点被加入到Q中时,相应的打印信息也被存储到QI中,在结点访问期间这些项被相继删除。

代码实现

#include <stdio.h>

#include <stdlib.h>

#define ElemType char

//结构体定义

typedef struct Node {

    ElemType data;

    struct Node *LChild;

    struct Node *RChild;

} BinNode, *BinTree;

typedef struct QueueNode {

    BinTree data;

    struct QueueNode *next;

} QueueNode;

typedef struct LinkQueue

{

    QueueNode *front;

    QueueNode *rear;

} LinkQueue;

/*

 * 结点的层次、打印位置、

 * 是否在同一层次和与输出的空格数

 */

typedef struct NodeInfo {

    int level;

    int pos;

    int enter;

    int spacenum;

} NodeInfo;

typedef struct QueueNodeInfo {

    NodeInfo data;

    struct QueueNodeInfo *next;

} QueueNodeInfo;

typedef struct NodeInfoQueue {

    QueueNodeInfo *front;

    QueueNodeInfo *rear;

} NodeInfoQueue;

//函数声明

int menu_select();

BinTree CreateBinTree(BinTree bt);

void LevelOrder(BinTree bt);

void InitQueue(LinkQueue *Q);

void EnQueue(LinkQueue *Q, BinTree bt);

BinTree DeQueue(LinkQueue *Q);

void InitQueueInfo(NodeInfoQueue *QI);

void EnQueueInfo(NodeInfoQueue *QI, NodeInfo NI);

NodeInfo DeQueueInfo(NodeInfoQueue *QI);

void DisplayTree(BinTree bt, int dataWidth, int screenWidth);


 

// main()函数

int main() {

    BinTree bt;

    int screenWidth=64;

    int dataWidth=2;

    

    for (;;) {

        switch(menu_select()) {

        case 1:

            printf("1、建立二叉树\n");

            bt = CreateBinTree(bt);

            break;

        case 2:

            printf("2、层次遍历\n");

            LevelOrder(bt);

            break;

        case 3:

            printf("3、显示二叉树\n");

            DisplayTree(bt, dataWidth, screenWidth);

            break;

        case 0:

            printf("再见!!\n");

            free(bt);

            return 0;

        }

    }

}

//菜单驱动程序

int menu_select() {

    int sn;

    printf("=========功能菜单============\n");

    printf("   1、建立二叉树\n");

    printf("   2、层次遍历\n");

    printf("   3、显示二叉树\n");

    printf("   0、退出程序\n");

    printf("==============================\n");

    printf("   请选择0--3:");

    for (;;) {

        scanf("%d", &sn);

        getchar();

        if (sn < 0 || sn > 3)

            printf("\n输入选择错误,请重新选择 0--3:");

        else

            break;

    }

    return sn;

}

/*

  TODO:

  功能描述:创建二叉树操作

  1、按扩展的先序次序输入二叉树中结点的值,

     #字符表示空子树,创建二叉链表表示的二叉树

     (BinTree) malloc(sizeof(BinNode)),

     将字符赋给节点中的data

  例:

    输入格式:AB##C##

    二叉树:

            A

          B   C

  参数:(BinTree bt) 树的指针

  返回值: BinTree 树的指针

*/

BinTree CreateBinTree(BinTree bt) {

char ch;

    BinTree p;

    p=bt;

    ch=getchar();

    if(ch=='#'){

        p=NULL;

    }

    else{

        p=(BinTree) malloc(sizeof(BinNode));

        p->data=ch;

        p->LChild=CreateBinTree(p);

        p->RChild=CreateBinTree(p);

    }

    return p;

}

/*

  TODO:

  功能描述:执行队列初始化操作

  参数:(LinkQueue *Q) 队列结构的指针

  返回值: void

*/

void InitQueue(LinkQueue *Q) {

    QueueNode *qNode = (QueueNode *) malloc(sizeof(QueueNode));

    Q->front = qNode;

    Q->rear = qNode;

    Q->front->next = NULL;

}

/*

  TODO:

  功能描述:执行入队操作

  参数:(LinkQueue *Q, BinTree bt) 队列结构的指针与入队元素值

  返回值: void

*/

void EnQueue(LinkQueue *Q, BinTree bt) {

    QueueNode *qNode = (QueueNode *) malloc(sizeof(QueueNode));

    qNode->data = bt;

    Q->rear->next = qNode;

    Q->rear = qNode;

}

/*

  TODO:

  功能描述:执行出队操作

  参数:(LinkQueue *Q, BinTree bt) 队列结构的指针与入队元素值

  返回值: BinTree 树的节点指针

*/

BinTree DeQueue(LinkQueue *Q) {

    QueueNode *qNode = Q->front->next;

    BinTree bt = qNode->data;

    Q->front->next = qNode->next;

    if (qNode == Q->rear) {

        Q->rear = Q->front;

    }

    free(qNode);

    return bt;

}

/*

  TODO:

  功能描述:执行队列初始化操作

  参数:(NodeInfoQueue *QI) 节点队列信息结构的指针

  返回值: void

*/

void InitQueueInfo(NodeInfoQueue *QI) {

    QueueNodeInfo *qNodeInfo = (QueueNodeInfo *) malloc(sizeof(QueueNodeInfo));

    QI->front = qNodeInfo;

    QI->rear = qNodeInfo;

    QI->front->next = NULL;

}

/*

  TODO:

  功能描述:执行入队操作

  参数:(NodeInfoQueue *QI, NodeInfo NI) 节点队列信息结构的指针与入队元素值

  返回值: void

*/

void EnQueueInfo(NodeInfoQueue *QI, NodeInfo NI) {

    QueueNodeInfo *qNodeInfo = (QueueNodeInfo *) malloc(sizeof(QueueNodeInfo));

    qNodeInfo->data = NI;

    QI->rear->next = qNodeInfo;

    QI->rear = qNodeInfo;

}

/*

  TODO:

  功能描述:执行出队操作

  参数:(NodeInfoQueue *QI) 队列结构的指针

  返回值: NodeInfo 树的节点的信息

*/

NodeInfo DeQueueInfo(NodeInfoQueue *QI) {

    QueueNodeInfo *qNodeInfo = QI->front->next;

    NodeInfo nInfo = qNodeInfo->data;

    QI->front->next = qNodeInfo->next;

    if (qNodeInfo == QI->rear) {

        QI->rear = QI->front;

    }

    free(qNodeInfo);

    return nInfo;

}

// 换行

void LineFeed() {

    printf("\n");

}

// 空格

void vBlank() {

    printf(" ");

}

// 数据展示

void Visit(ElemType data) {

    printf("%2c", data);

}

/*

  TODO:

  功能描述:层次遍历二叉树操作

  1、从根节点开始,从上至下逐层遍历,

  在同一层中,从左到右逐一显示。

  2、显示全部节点末尾调用换行函数

  3、换行:

     调用函数 void LineFeed()

     数据打印:

     调用函数 void Visit(ElemType data)

  参数: (BinTree bt) 树的指针

  返回值: void

*/

void LevelOrder(BinTree bt) {

if(bt==NULL) {

        printf("空");

        return;

    }

    LinkQueue q;

    BinTree p;

    InitQueue(&q);

    EnQueue(&q,bt);

    while(q.front!=q.rear){

        p=DeQueue(&q);

        Visit(p->data);

        if(p->LChild) EnQueue(&q,p->LChild);

        if(p->RChild) EnQueue(&q,p->RChild);

    }

    LineFeed();

}

/*

  TODO:

  功能描述:显示二叉树操作

  1、从根节点开始,从上至下逐层遍历,

  在同一层中,从左到右逐一访问。

  2、数据值的最大宽度dataWidth=2以及屏幕的宽度screenWidth=64。

  第i层的偏移量(offset)为screenWidth/2的i次幂。

  第i层的每个结点的位置是访问第i-1层其双亲结点时确定的。

  假设其双亲位置是(i-1,parentPos)。

  若其第i层的结点是左孩子,那么它的位置是(i,parentPos-offset);

  若是右孩子则位置为(i,parentPos+offset)。

  3、用两个队列和按层遍历树中的结点。

  队列LinkQueue中存放结点,二队列NodeInfoQueue中则以记录类型Info存放结点的层次和打印位置。

  当结点被加入到LinkQueue中时,相应的打印信息也被存储到NodeInfoQueue中

  4、显示全部节点末尾调用换行函数

  5、换行:

     调用函数 void LineFeed()

     空格:

     调用函数 void vBlank()

     数据打印:

     调用函数 void Visit(ElemType data)

  参数:(BinTree bt, int dataWidth, int screenWidth) 树的指针数据值的最大宽度与屏幕的宽度

  返回值:void

*/

void  DisplayTree(BinTree bt, int dataWidth, int screenWidth) {

if(!bt) return;

        LineFeed();

        dataWidth=2;

        screenWidth=64;

        int j,n,set,i;

        BinTree e=bt,t=bt;

        NodeInfo NI;

        LinkQueue Q1;

        NodeInfoQueue Q2;

        InitQueue(&Q1);

        InitQueueInfo(&Q2);

        EnQueue(&Q1,bt);

        NI.level=1;

        NI.pos=screenWidth/pow(2,NI.level);

        n=NI.pos;

        j=1;

        NI.enter=1;

        NI.spacenum=NI.pos-1;

        EnQueueInfo(&Q2, NI);

        e=DeQueue(&Q1);

        NI=DeQueueInfo(&Q2);

        for(i=1;i<n-1;i++) vBlank();

        Visit(e->data);

        while(1){

                if(e->LChild){

                EnQueue(&Q1,e->LChild);

                NI.level++;

                NI.enter++;

                set=screenWidth/pow(2,NI.level);

                NI.pos=n-set;

                NI.spacenum=NI.pos-1;

                EnQueueInfo(&Q2, NI);

            }

            if(e->RChild){

                if(!e->LChild){

                    NI.level++;

                    NI.enter++;

                }

                set=screenWidth/pow(2,NI.level);

                NI.pos=n+set;

                NI.spacenum=NI.pos-1;

                EnQueue(&Q1,e->RChild);

                EnQueueInfo(&Q2, NI);

            }

            e=DeQueue(&Q1);

            NI=DeQueueInfo(&Q2);

            if(NI.level==j+1){

              LineFeed();

              for(i=1;i<NI.pos-1;i++)

                  vBlank();

            }

            else{

                for(i=1;i<NI.pos-n-1;i++) vBlank();

             }

            Visit(e->data);

            n=NI.pos;

            j=NI.enter;

            if(Q1.front==Q1.rear&&e->LChild==NULL&&e->RChild==NULL){

               LineFeed();

               return 0 ;

            }

         }

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值