数据结构——新建一棵完全二叉树(C语言动态实现)

一、动态实现一棵完全二叉树

1、构建数据结构体

总共构建三个数据结构体:
①树节点结构体:TreeNode,用于存放节点的数据和左右孩子指针
②队列节点结构体:QueueNode,用于存放树的节点信息和下一个节点的指针
③队列信息结构体:QueueInfo,用于存放队列的头结点、尾节点以及队列的大小,在节点入队和出队时,维护这个队列信息结构体
在这里插入图片描述

typedef struct TreeNode{//二叉树节点
    int Data;//二叉树数据
    struct TreeNode* left;//二叉树左孩子指针
    struct TreeNode* right;//二叉树右孩子指针
}TreeNode;

typedef struct QueueNode{//队列节点
    struct TreeNode* treeNode;//树节点指针
    struct QueueNode* next;//下一个队列节点指针
}QueueNode;

typedef struct QueueInfo{//队列信息节点
    struct QueueNode * front, *rear;// 队列的头、尾节点指针
    int size;
}QueueInfo;

2、往队列里面插入节点
①初始化一个队列
在这里插入图片描述

QueueInfo* InitQueue(){//创建一个仅有头结点的队列
    QueueInfo* info = (QueueInfo*)malloc(sizeof(QueueInfo));//申请一块用于保存队列信息结构体的内存空间
    info->front =info->rear=(QueueNode*)malloc(sizeof(QueueNode));//申请一个队列节点空间,队列信息结构体的头尾指针均指向这个队列节点
    info->size = 0;//队列的头节点不包括在内,所以队列的长度为0
    info->front->next =NULL;//初始化后,队列头节点的next指针不指向任何节点
    info->front->treeNode = NULL;//队列头结点的treeNode节点为空
    return info;//返回队列信息结构体
}

②往队列中插入一个节点
在这里插入图片描述

void AddToQueue(QueueInfo* info, TreeNode* treeNode){//将一个新的队列节点添加到队列
     info->size++;//队列长度+1
     info->rear->next = (QueueNode*)malloc(sizeof(struct QueueNode));//将队列尾节点next指针指向新的QueueNode节点
     info->rear=info->rear->next;//更新rear尾指针,指向新的尾节点
     info->rear->treeNode = treeNode;//将QueueNode中的treeNode数据更新为传入的TreeNode节点
     info->rear->next = NULL;//将QueueNode中的next指针置位空
}

③再插入一个节点
在这里插入图片描述
实现过程同步骤②一致

3、队列节点出队
①当size>1时,从队列中删除一个节点
在这里插入图片描述
②当size=1时,从队列中删除一个节点
在这里插入图片描述
③当size=0时,返回为空

TreeNode* DeleteFromQueue(QueueInfo* info){//从队列中删除一个头节点
    TreeNode* treeNode=NULL;//新建一个空树节点保存出队的树节点
    if(info->front==info->rear){return NULL;}//如果为空队列,返回为空
    if(info->front->next==info->rear){//如果除头结点外仅有一个队列节点
        info->size--;//队列长度为-1
        QueueNode* temp=info->front->next;
        treeNode=info->front->next->treeNode;//树节点赋值为出队的树节点
        info->rear=info->front;//出队后前后指针均指向头队节点
        info->front->next=NULL;//队头节点的next指针为空
        free(temp);//释放出队的节点空间
        return treeNode;//返回树节点
    }else{//如果队列的节点除头结点外多于1个
        info->size--;//队列长度为-1
        QueueNode* temp=info->front->next;//temp保存出队的QueueNode地址信息,用于后面的释放空间
        treeNode=info->front->next->treeNode;//树节点赋值为出队的树节点
        info->front->next=info->front->next->next;//队列头结点的next指针指向下下个队列节点
        free(temp);//释放出队的节点的空间
        return treeNode;//返回树节点
    }
}

4、创建完全二叉树

树节点的入队出队过程如下图所示:在这里插入图片描述

TreeNode* CreateCompleteTree(){//创建一棵完全二叉树并返回树的根节点
    int data;//创建一个临时存放数据的data变量
    TreeNode *rootNode,*treeNode;//新建树的根节点和其它节点
    QueueInfo* info = InitQueue();//创建一个空队列
    scanf("%d", &data);//输入data数据
    if (data != END){//如果data不为-1
        rootNode = (TreeNode*)malloc(sizeof(struct TreeNode));//申请二叉树的根节点内存空间
        rootNode->data = data;//根节点data赋值
        rootNode->left = rootNode->right = NULL;//根节点的左右孩子指针为空
        AddToQueue(info, rootNode);//将根节点入队
    }else{
       return NULL; //如果一开始就输入-1,则不创建树根节点,函数结束
    }
    while (info->size != 0){//如果队列不为空
        treeNode= DeleteFromQueue(info);//队列中的元素出队,第一次出队的是根节点
        scanf("%d", &data);
        if (data == END){//如果data=-1,则树节点没有左右孩子
            treeNode->left = NULL;
            treeNode->right = NULL;
            return rootNode;//return可以直接跳出当前循环,函数结束
        }else{//若data!=-1,则创建节点的左子树
            treeNode->left = (TreeNode*)malloc(sizeof(TreeNode));//为节点的左子树节点申请空间并赋值
            treeNode->left->data = data;//左子树节点的data赋值
            treeNode->left->left = treeNode->left->right = NULL;//左子树节点的左右孩子指针均为空
            AddToQueue(info, treeNode->left);//将左子树节点入队
        }
        scanf("%d", &data);
        if (data == END){//如果data=-1,则节点没有右子树节点
            treeNode->right = NULL;
            return rootNode;//return跳出当前循环,函数结束
        }else{
            treeNode->right = (TreeNode*)malloc(sizeof(TreeNode));//为节点的右子树节点申请空间并赋值
            treeNode->right->data = data;//右子树节点的data赋值
            treeNode->right->left = treeNode->right->right = NULL;//右子树节点的左右孩子指针均为空
            AddToQueue(info, treeNode->right);//右子树节点入队
        }
        //继续循环输入,直到输入-1,则跳出当前循环并结束结束createCompleteTree()函数
    }
    return rootNode;//返回树根节点
}

5、完全二叉树的层序遍历
完全二叉树的层序遍历依旧使用队列,代码如下:

void LevelTraversal(TreeNode* rootNode){//二叉树的层序遍历
    QueueInfo* info=InitQueue();//新建一个队列
    AddToQueue(info,rootNode);//根节点入队
    TreeNode* treeNode=NULL;
    while(1){
        treeNode=DeleteFromQueue(info);//出队的节点赋值给新建的树节点,第一轮为树根节点
        if(treeNode==NULL){//节点为空
            return;//跳出循环,结束层序遍历函数
        }else{//节点不为空
            if(treeNode->left!=NULL){//节点的左孩子节点不为空
                AddToQueue(info,treeNode->left);//左孩子节点入队
                if(treeNode->right!=NULL){//右孩子节点不为空
                    AddToQueue(info,treeNode->right);//右孩子节点入队
                }//结束if
            }//结束if
            printf("%d ",treeNode->data);//打印出队的节点的data值
        }
    }

}

6、完全二叉树的中序遍历

void inOrder(TreeNode* treeNode){
    if(treeNode==NULL)return;
    inOrder(treeNode->left);
    printf(" %d",treeNode->data);
    inOrder(treeNode->right);
}

7、完整程序

#include<stdio.h>
#include<stdlib.h>
#define END -1//如果输入是-1,则输入结束
typedef struct TreeNode{//二叉树节点
    int data;//二叉树数据
    struct TreeNode* left;//二叉树左孩子指针
    struct TreeNode* right;//二叉树右孩子指针
}TreeNode;
typedef struct QueueNode{//队列节点
    struct TreeNode* treeNode;//树节点指针
    struct QueueNode* next;//下一个队列节点指针
}QueueNode;
typedef struct QueueInfo{//队列信息节点
    struct QueueNode * front, *rear;// 队列的头、尾节点指针
    int size;
}QueueInfo;
QueueInfo* InitQueue(){//创建一个仅有头结点的队列
    QueueInfo* info = (QueueInfo*)malloc(sizeof(QueueInfo));//申请一块用于保存队列信息结构体的内存空间
    info->front =info->rear=(QueueNode*)malloc(sizeof(QueueNode));//申请一个队列节点空间,队列信息结构体的头尾指针均指向这个队列节点
    info->size = 0;//队列的头节点不包括在内,所以队列的长度为0
    info->front->next =NULL;//初始化后,队列头节点的next指针不指向任何节点
    info->front->treeNode = NULL;//队列头结点的treeNode节点为空
    return info;//返回队列信息结构体
}
void AddToQueue(QueueInfo* info, TreeNode* treeNode){//将一个新的队列节点添加到队列
     info->size++;//队列长度+1
     info->rear->next = (QueueNode*)malloc(sizeof(struct QueueNode));//将队列尾节点next指针指向新的QueueNode节点
     info->rear=info->rear->next;//更新rear尾指针,指向新的尾节点
     info->rear->treeNode = treeNode;//将QueueNode中的treeNode数据更新为传入的TreeNode节点
     info->rear->next = NULL;//将QueueNode中的next指针置位空
}
TreeNode* DeleteFromQueue(QueueInfo* info){//从队列中删除一个头节点
    TreeNode* treeNode=NULL;//新建一个空树节点保存出队的树节点
    if(info->front==info->rear){return NULL;}//如果为空队列,返回为空
    if(info->front->next==info->rear){//如果除头结点外仅有一个队列节点
        info->size--;//队列长度为-1
        QueueNode* temp=info->front->next;
        treeNode=info->front->next->treeNode;//树节点赋值为出队的树节点
        info->rear=info->front;//出队后前后指针均指向头队节点
        info->front->next=NULL;//队头节点的next指针为空
        free(temp);//释放出队的节点空间
        return treeNode;//返回树节点
    }else{//如果队列的节点除头结点外多于1个
        info->size--;//队列长度为-1
        QueueNode* temp=info->front->next;//temp保存出队的QueueNode地址信息,用于后面的释放空间
        treeNode=info->front->next->treeNode;//树节点赋值为出队的树节点
        info->front->next=info->front->next->next;//队列头结点的next指针指向下下个队列节点
        free(temp);//释放出队的节点的空间
        return treeNode;//返回树节点
    }

}
void LevelTraversal(TreeNode* rootNode){//二叉树的层序遍历
    QueueInfo* info=InitQueue();//新建一个队列
    AddToQueue(info,rootNode);//根节点入队
    TreeNode* treeNode=NULL;
    while(1){
        treeNode=DeleteFromQueue(info);//出队的节点赋值给新建的树节点,第一轮为树根节点
        if(treeNode==NULL){//节点为空
            return;//跳出循环,结束层序遍历函数
        }else{//节点不为空
            if(treeNode->left!=NULL){//节点的左孩子节点不为空
                AddToQueue(info,treeNode->left);//左孩子节点入队
                if(treeNode->right!=NULL){//右孩子节点不为空
                    AddToQueue(info,treeNode->right);//右孩子节点入队
                }//结束if
            }//结束if
            printf("%d ",treeNode->data);//打印出队的节点的data值
        }
    }
}
TreeNode* CreateCompleteTree(){//创建一棵完全二叉树并返回树的根节点
    int data;//创建一个临时存放数据的data变量
    TreeNode *rootNode,*treeNode;//新建树的根节点和其它节点
    QueueInfo* info = InitQueue();//创建一个空队列
    scanf("%d", &data);//输入data数据
    if (data != END){//如果data不为-1
        rootNode = (TreeNode*)malloc(sizeof(struct TreeNode));//申请二叉树的根节点内存空间
        rootNode->data = data;//根节点data赋值
        rootNode->left = rootNode->right = NULL;//根节点的左右孩子指针为空
        AddToQueue(info, rootNode);//将根节点入队
    }else{
       return NULL; //如果一开始就输入-1,则不创建树根节点,函数结束
    }
    while (info->size != 0){//如果队列不为空
        treeNode= DeleteFromQueue(info);//队列中的元素出队,第一次出队的是根节点
        scanf("%d", &data);
        if (data == END){//如果data=-1,则树节点没有左右孩子
            treeNode->left = NULL;
            treeNode->right = NULL;
            return rootNode;//return可以直接跳出当前循环,函数结束
        }else{//若data!=-1,则创建节点的左子树
            treeNode->left = (TreeNode*)malloc(sizeof(TreeNode));//为节点的左子树节点申请空间并赋值
            treeNode->left->data = data;//左子树节点的data赋值
            treeNode->left->left = treeNode->left->right = NULL;//左子树节点的左右孩子指针均为空
            AddToQueue(info, treeNode->left);//将左子树节点入队
        }
        scanf("%d", &data);
        if (data == END){//如果data=-1,则节点没有右子树节点
            treeNode->right = NULL;
            return rootNode;//return跳出当前循环,函数结束
        }else{
            treeNode->right = (TreeNode*)malloc(sizeof(TreeNode));//为节点的右子树节点申请空间并赋值
            treeNode->right->data = data;//右子树节点的data赋值
            treeNode->right->left = treeNode->right->right = NULL;//右子树节点的左右孩子指针均为空
            AddToQueue(info, treeNode->right);//右子树节点入队
        }
        //继续循环输入,知道输入-1,则跳出当前循环并结束结束createCompleteTree()函数
    }
    return rootNode;
}

void inOrder(TreeNode* treeNode){
    if(treeNode==NULL)return;
    inOrder(treeNode->left);
    printf(" %d",treeNode->data);
    inOrder(treeNode->right);
}
int main()
{
    TreeNode* treeNode = CreateCompleteTree();//输入一个元素就回一次车
    LevelTraversal(treeNode);
    printf("\n");
    inOrder(treeNode);
    return 0;
}

8、程序测试

//输入数据如下:
1
2
3
4
5
6
7
8
9
-1
层序遍历:1 2 3 4 5 6 7 8 9
中序遍历:8 4 9 2 5 1 6 3 7
Process finished with exit code 0

  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

karwen2020

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值