二叉树上删除叶子结点用以建立双向链表(C语言实现)

已知一棵以二叉链表存储的二叉树,按从右到左的次序,利用叶子结点建立一个带头结点的双向链表。

(1)从键盘输入扩展的先序结点数据,建立二叉树。

(2)找叶子结点,找到后从二叉树中删除,然后插入到双向链表中。

(3)要求程序通过一个主菜单进行控制,通过选择菜单项序号调用各功能函数。

[提示]

前序、中序、后序遍历二叉树,访问叶子结点的相对次序都是从左至右。因此,可以采用头插法建立双向链表。

树的二叉链表的结点与双向链表的结点结构相同,只是对两个指针域的解释不同。

typedef struct tree {

char data;

struct tree *Llink;//左儿子 前驱节点

struct tree *Rlink;//右儿子 后继节点

struct tree *Parent;//父节点

} BTNode,*BinTree,DNode,*DoubleList;

测试数据,其扩展的先序遍历序列为ABC..DE.G..F..H..,建立二叉树的遍历叶子结点的次序为:C G F H。 所建立的双向链表为:

L-> <=>H<=>F<=>G<=>C

代码实现

#include<stdio.h>

#include<stdlib.h>

#include <math.h>

#define TRUE 1

#define FALSE 0

#define MAXSIZE 50

#define scanf_s scanf

//二叉树节点

typedef struct tree {

    char data;

    struct tree *Llink;//左儿子 前驱节点

    struct tree *Rlink;//右儿子 后继节点

    struct tree *Parent;//父节点

} BTNode,*BinTree,DNode,*DoubleList;


 

int Select_menu() {

    int sn;

    printf("          二叉树打印\n");

    printf("-------------------------------------------\n");

    printf("1.利用扩展的先序序列建立二叉树\n");

    printf("2 找叶子结点,找到后从二叉树中删除,然后插入到双向链表中并打印 。\n");

    printf("0.退出系统\n");

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

    printf("  请输入0--2: \n");

    for (;;) {

        scanf_s("%d", &sn);

        getchar();

        if (sn > 2 || sn < 0)

            printf("输入错误,请重新输入:\n");

        else

            break;

    }

    return sn;

}

//按照层打印二叉树

void PrintBinTree(BinTree bt) {

    if(bt==NULL){

        printf("树为空\n");

        return;

    }

    printf("按照层打印二叉树为:");

    BinTree p;

    BinTree *q[MAXSIZE];

    int head = 0, tail = 0;

    if (bt) {

        tail = (tail + 1) % MAXSIZE;

        q[tail] = bt;

    }

    while (head != tail) {

        head = (head + 1) % MAXSIZE;

        p = q[head];

        printf("%c ", p->data);

        if (p->Llink != NULL) {

            tail = (tail + 1) % MAXSIZE;

            q[tail] = p->Llink;

        }

        if (p->Rlink != NULL) {

            tail = (tail + 1) % MAXSIZE;

            q[tail] = p->Rlink;

        }

    }

    printf("\n");

    return;

}

//打印循双向链表

void PrintDoubleList(DoubleList dl) {

    if(dl==NULL || dl->Rlink==NULL){

        printf("双向链表为空\n");

        return;

    }

    printf("双向环链为:");

    //遍历链表,表头不存值

    while (dl->Rlink) {

        dl = dl->Rlink;

        printf("%c ", dl->data);

    }

    printf("\n");

    return;

}

//按照层打印二叉树后打印循双向链表

void printAll(BinTree bt,DoubleList dl){

    PrintBinTree(bt);

    PrintDoubleList(dl);

}


 

//叶子节点个数

int LeafCount(BTNode* pnode)

{

    static int count;

    if (pnode != NULL)

    {

        if (pnode->Llink == NULL && pnode->Rlink == NULL)

        {

            count++;

        }

        LeafCount(pnode->Llink);

        LeafCount(pnode->Rlink);

    }

    return count;

}

//计算二叉树深度

int treeDepth(BTNode *t) {

    int deep = 0;

    int left = 0;

    int right = 0;

    if (t == NULL) {

        return deep;

    }

    left = treeDepth(t->Llink);

    right = treeDepth(t->Rlink);

    deep = right > left ? right : left;

    deep++;

    return deep;

}

/*

 TODO: 从键盘输入扩展的先序结点数据,建立二叉树。

 功能描述:从键盘输入扩展的先序结点数据,建立二叉树,空节点用.表示输入

 参数说明:bt-BinTree指针 表示树根

 返回值说明:无

 比如输入:ACE.F...D.B.G..

 对应的树结构为

   A

  C D

E    B

 F    G

 */

void CreateTree(BinTree *bt) {

    char ch;

    ch=getchar();

    if(ch=='.') *bt=NULL;

    else

    {

        *bt=(BinTree)malloc(sizeof(BTNode));

        if(*bt==NULL) return 0;

        (*bt)->data=ch;

        CreateTree(&(*bt)->Llink);

        CreateTree(&(*bt)->Rlink);

    }

    return;

}

/*

 TODO: 设置二叉树父亲节点。

 功能描述:给各个结点的parent赋值,设置成父亲节点。

 参数说明:p-BinTree指针 表示当前节点

           q-BinTree指针 表示当前节点的父节点

 返回值说明:无

 */

void setParent(BinTree p,BinTree q)

{

    if(p==NULL) return ;

    else{

        if(p->Llink!=NULL) p->Llink->Parent=p;

        if(p->Rlink!=NULL) p->Rlink->Parent=p;

        }

        setParent(p->Llink,q);

        setParent(p->Rlink,q);

}

/*

已知一棵以二叉链表存储的二叉树,按从右到左的次序,利用叶子结点建立一个带头结点的双向链表。

 功能描述:建立双向链表,调用LeafTraversal遍历二叉树,删除叶子节点,用头插法插入到双向链表中,调用printAll打印二叉树和双向链表

 参数说明:bt-BinTree指针 表示树根

       dl-DoubleList指针 表示链表

 返回值说明:无

 比如输入:其扩展的先序遍历序列为ABC..DE.G..F..H..,建立二叉树的遍历叶子结点的次序为:C G F H。 所建立的双向链表为:

L-> <=>H<=>F<=>G<=>C

*/

void CreateDListFromLeaves(BinTree bt,DoubleList dl){

    //新建带头结点的双向链表

    dl = (DoubleList)malloc(sizeof(DNode));

    dl->data=0;

    dl->Llink=NULL;

    dl->Rlink=NULL;

    //查出叶子节点个数

    int leaves = LeafCount(bt);

    int depth = treeDepth(bt);

    if(leaves == 0 || depth==1){

        printAll(bt,dl);

        return;

    }

    //从二叉树中删除,然后用头插法插入到双向链表中

    LeafTraversal(bt,dl,2);

    //遍历二叉树叶子节点,用头插法插入到双向链表中

    printAll(bt,dl);

}

/*

 TODO: 遍历二叉树,删除叶子节点,并插入到双向链表

 功能描述:找叶子结点,找到后从二叉树中删除(删除是通过该节点的父指针找到父节点,然后根据是左子树还是右子树设置父节点的左或右儿子为NULL),

           然后用头插法插入到双向链表中。

 参数说明:pnode-BTNode指针 表示树根

       dl-DoubleList指针 表示链表

       LeftOrRight-int 0 表示遍历左子树  1 表示遍历右子树  2 表示根

 返回值说明:无

 比如输入:其扩展的先序遍历序列为ABC..DE.G..F..H..,建立二叉树的遍历叶子结点的次序为:C G F H。 所建立的双向链表为:

L-> <=>H<=>F<=>G<=>C

*/

void LeafTraversal(BTNode* pnode,DoubleList dl,int LeftOrRight)

{

   if(NULL==pnode) return;

   LeafTraversal(pnode->Llink,dl,0);

   if(pnode->Llink==NULL&&pnode->Rlink==NULL&&pnode->data!='P')

   {

       if(LeftOrRight==0) pnode->Parent->Llink=NULL;

       else if(LeftOrRight==1) pnode->Parent->Rlink=NULL;

       DoubleList s;

       s = (DoubleList)malloc(sizeof(DNode));

       s->data=pnode->data;

       s->Rlink=dl->Rlink;

       dl->Rlink=s;

   }

    LeafTraversal(pnode->Rlink,dl,1);

    return;

}

int main(void) {

    BinTree bt = NULL;

    DoubleList dl = NULL;

    BinTree *btnull = NULL;

        for (;;) {

            switch (Select_menu()) {

            case 1:

                printf("请使用前序遍历的方式输入字符串,用.代替空值\n");

                CreateTree(&bt);

                setParent(bt,btnull);

                break;

            case 2:

                CreateDListFromLeaves(bt,dl);

                break;

            case 0:

                printf("再见!\n");

                free(bt);

                free(dl);

                return 0;

            }

        }

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值