【数据结构】C语言实现链式二叉树(附完整运行代码)

🦄个人主页:修修修也

🎏所属专栏:数据结构

⚙️操作环境:Visual Studio 2022


目录

一.了解项目功能

二.项目功能演示

三.逐步实现项目功能模块及其逻辑详解

1.实现链式二叉树程序菜单

2.实现链式二叉树程序功能可循环使用

3.实现链式二叉树的新结点创建

4.实现链式二叉树的先序建树

5.实现链式二叉树的判空

6.实现链式二叉树的先序遍历

7.实现链式二叉树的中序遍历

8.实现链式二叉树的后序遍历

9.实现链式二叉树的层序遍历

10.实现链式二叉树的叶子节点数计算

11.实现链式二叉树左孩子节点数计算

12.实现链式二叉树右孩子节点数计算

13.实现链式二叉树节点数计算

14.实现链式二叉树高度计算

15.实现链式二叉树查询某层结点个数

16.实现链式二叉树查询某节点是否存在

17.实现链式二叉树判断是否为完全二叉树

18.实现翻转链式二叉树

19.实现链式二叉树的销毁

四.项目完整代码

BTree.c文件

 Queue.c 文件

Queue.h文件

结语


一.了解项目功能

在本次项目中我们的目标是实现一个链式二叉树:

链式二叉树使用动态内存分配空间,可以用来存储任意数量的同类型数据.

二叉树结点(BTNode)需要包含三个要素:左孩子指针域left,数据域data,右孩子指针域right.

二叉树结点(BTNode)逻辑结构图示如下:

 链式二叉树程序提供的功能有:

  1. 二叉树的先序建树.
  2. 二叉树的新节点创建.
  3. 二叉树的判空
  4. 二叉树的先序遍历
  5. 二叉树的中序遍历
  6. 二叉树的后序遍历
  7. 二叉树的层序遍历
  8. 二叉树的叶子结点数
  9. 二叉树的左孩子节点数
  10. 二叉树的右孩子节点数
  11. 二叉树的结点数
  12. 二叉树的高度
  13. 查询二叉树某层的结点个数
  14. 查询某节点是否在二叉树中
  15. 查询此树是否是完全二叉树
  16. 翻转二叉树
  17. 销毁二叉树

二.项目功能演示

要编写一个链式二叉树项目,首先要明确我们想要达到的效果是什么样,下面我将用vs2022编译器来为大家演示一下链式二叉树程序运行时的样子:

C语言实现l二叉树程序功能演示


三.逐步实现项目功能模块及其逻辑详解

通过第二部分对项目功能的介绍,我们已经对链式二叉树的功能有了大致的了解,虽然看似需要实现的功能很多,貌似一时间不知该如何下手,但我们可以分步分模块来分析这个项目的流程,最后再将各部分进行整合,所以大家不用担心,跟着我一步一步分析吧!


!!!注意,该部分的代码只是为了详细介绍某一部分的项目实现逻辑,故可能会删减一些与该部分不相关的代码以便大家理解,需要查看或拷贝完整详细代码的朋友可以移步本文第四部分。

为直观分析链式二叉树中的递归思路,在后续的功能实现部分,我会频繁使用下面这棵树画函数递归展开图,大家可以先熟悉一下这棵二叉树,我们马上就开始实现具体的功能:


1.实现链式二叉树程序菜单

菜单部分的逻辑比较简单,就是利用C语言printf函数打印出这个菜单界面即可。但要注意菜单的标序要和后续switch...case语句的分支相应,以免导致后续执行语句错乱的问题.基础问题就不过多赘述了,代码如下:

该部分功能实现代码如下: 

//菜单二叉树
void BTMenu()
{printf("****************************************\n");printf("******请选择要进行的操作          ******\n");printf("******1.先序建树                  ******\n");printf("******2.查询树是否为空            ******\n");printf("******3.树的先序遍历              ******\n");printf("******4.树的中序遍历              ******\n");printf("******5.树的后序遍历              ******\n");printf("******6.树的层序遍历              ******\n");printf("******7.树的叶子节点数            ******\n");printf("******8.树的左孩子节点个数        ******\n");printf("******9.树的右孩子节点个数        ******\n");printf("******10.树的节点个数             ******\n");printf("******11.树的高度                 ******\n");printf("******12.查询树某层的节点个数     ******\n");printf("******13.查询结点是否在树中       ******\n");printf("******14.查询树是否是完全二叉树   ******\n");printf("******15.翻转此树                 ******\n");printf("******0.退出二叉树程序            ******\n");printf("****************************************\n");printf("请选择:>");
}

2.实现链式二叉树程序功能可循环使用

由于我们要实现链式二叉树的功能可以反复使用的逻辑,且至少在一开始执行一次,因此我们选择do...while的循环语句来实现这一部分的逻辑.

该部分功能实现代码如下:

int main()
{BTNode* root = NULL;int swi = 0;do         {BTMenu();scanf("%d", &swi);switch (swi){case 0:TreeDestory(root); // 释放树内存printf("您已退出程序:>\n");break;case 1:printf("请输入树:>(以'#'表示空孩子)\n");char a[100];scanf("%s", a);int i = 0;root = CreatTree(a, &i);printf("已成功建树:>\n");break;case 2:if (BTEmpty(root) == true) //判断树空printf("当前树为空\n");elseprintf("当前树不为空\n");break;case 3:printf("先序遍历此树: ");    PreOrder(root);printf("\n");break;case 4:printf("中序遍历此树: ");InOrder(root);printf("\n");break;case 5:printf("后序遍历此树: ");PostOrder(root);printf("\n");break;case 6:printf("层序遍历此树: ");LevelOrder(root);printf("\n");break;case 7:printf("此树的叶子节点数有: %d\n", BinaryTreeLeafSize(root));break;case 8:printf("此树的左孩子节点数有: %d\n", BinaryTreeLLeafSize(root));break;case 9:printf("此树的右孩子节点数有: %d\n", BinaryTreeRLeafSize(root));break;case 10:printf("此树的节点数有:%d\n", TreeSize(root));break;case 11:printf("此树的高度为:%d\n", TreeHeight(root));break;case 12:printf("请输入你要查询的层数:>");int k = 0;scanf("%d", &k);printf("此树第%d层的节点数有:%d\n",k,TreeKLevel(root,k));break;case 13:printf("请输入你要查询的结点:>");setbuf(stdin, NULL);//之前键盘缓冲区有污染,该函数作用是清空键盘缓冲区BTDataType x = 0;scanf("%c", &x);//二叉树查找值为x的结点if (BinaryTreeFind(root, x) != NULL){printf("结点%c在树中:>\n",x);}else{printf("结点%c不在树中:<\n",x);}break;case 14:if (TreeComplete(root)){printf("该树是完全二叉树:>\n");}else{printf("该树不是完全二叉树:<\n");}break;case 15:root = InvertTree(root);printf("翻转成功:>\n");printf("前序遍历结果为:");PreOrder(root);printf("\n");break;default:printf("输入错误,请重新输入\n");break;}} while (swi);return 0;
}

3.实现链式二叉树的新结点创建

创建链式二叉树结点的结构体应该包括:存储数据的数据域data,以及存储左孩子结点地址的指针域left,存储右孩子结点地址的指针域right.

图示如下:

因此我们创建BTNode结构体类型时应由一个数据成员类型及两个指向该结构体的结构体的指针组成.

了解了链式二叉树的结点构造后,创建新结点就和之前单链表中对新结点的处理方法相同了,

具体思路如下:

  1. 使用malloc动态开辟结点空间
  2. 对结点结构体内容进行初始化
  3. 处理完毕,返回该新结点

该部分代码实现如下:

//获取二叉树结点
BTNode* BuyNode(BTDataType x)
{BTNode* node = (BTNode*)malloc(sizeof(BTNode));if (node == NULL){perror("malloc fail::");return NULL;}node->data = x;node->left = NULL;node->right = NULL;return node;
}

4.实现链式二叉树的先序建树

先序建树部分,我们采用的思路是:

  1. 先在主函数接收先序排列的建树字符串
  2. 再将其传入建树函数递归建树

我们按照最开始画的那颗树先前序遍历树:

我们给函数输入我们前序遍历的树得到的字符串,此时就可以画出前序建树时的函数递归展开图:

该部分实现代码如下:

//前序建树
BTNode* CreatTree(char* a, int* pi)
{if (a[(*pi)] == '#')  //不是#不++!{(*pi)++;return NULL;}BTNode* root = BuyNode(a[(*pi)]);(*pi)++;root->left = CreatTree(a, pi);root->right = CreatTree(a, pi);return root;
}//主函数部分调用建树逻辑
case 1:printf("请输入树:>(以'#'表示空孩子)\n");char a[100];scanf("%s", a);int i = 0;root = CreatTree(a, &i);printf("已成功建树:>\n");break;

5.实现链式二叉树的判空

链式二叉树的判空逻辑较为简单,只需要返回树根节点的状态即可.

该部分实现代码如下:

//判断树空
bool BTEmpty(BTNode* root)
{return (!root);
}

6.实现链式二叉树的先序遍历

先序遍历的思路是:

  1. 先访问根节点
  2. 再递归访问左子树
  3. 再递归访问右子树

 我们画一下先序遍历的函数递归展开图:

该部分代码实现如下:

//前序遍历
void PreOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}printf("%c ", root->data);PreOrder(root->left);PreOrder(root->right);
}

7.实现链式二叉树的中序遍历

中序遍历的思路是:

  1. 先递归访问左子树
  2. 再访问根节点
  3. 最后递归访问右子树

该部分递归展开图于先序遍历类似,故不作展示,代码实现如下:

//中序遍历
void InOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}InOrder(root->left);printf("%c ", root->data);InOrder(root->right);
}

8.实现链式二叉树的后序遍历

后序遍历的思路是:

  1. 先递归访问左子树
  2. 再递归访问右子树
  3. 最后访问根节点

该部分递归展开图于先序遍历类似,故不作展示,代码实现如下:

//后序遍历
void PostOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}PostOrder(root->left);PostOrder(root->right);printf("%c ", root->data);
}

9.实现链式二叉树的层序遍历

实现层序遍历的思路为(借助队列实现):

  1. 将根节点入队
  2. 如果队首结点不为空,将队首结点不为空的孩子入队
  3. 访问队首元素并将其出队

注:因为在之前的文章中我们已经实现过链队列程序了,所以在树部分我们不涉及讲解链队列的实现,而是直接使用.

 层序遍历算法演示如下:

该部分实现代码如下(链队列的实现在文末的两个Queue文件中):

//层序遍历(借助队列)
void LevelOrder(BTNode* root)
{Que q;QueueInit(&q);if (root)QueuePush(&q, root);while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);printf("%c ", front->data);if (front->left != NULL){QueuePush(&q, front->left);}if (front->right != NULL){QueuePush(&q, front->right);}}QueueDestroy(&q);
}

10.实现链式二叉树的叶子节点数计算

叶子结点的计算上我们采用递归分治的思想,即

根结点的叶子节点数=左子树的叶子节点数+右子树的叶子节点数.

当然,叶子结点的判断条件根存在且左右子树都为空.

函数的递归展开图如下:

该部分代码实现如下:

//叶子节点数
int BinaryTreeLeafSize(BTNode* root)
{if (root == NULL)return 0;if (root->left==NULL && root->right==NULL)//左右子树都为空就是叶子结点{return 1;}return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);//返回每颗子树的左右叶子节点数
}

11.实现链式二叉树左孩子节点数计算

左孩子结点的计算上我们同样采用递归分治的思想,即

根结点的左孩子节点数=左子树的左孩子节点数+右子树的左孩子节点数.

当然,左孩子结点的判断条件根存在且左子树不为空.

该函数的递归展开和叶子节点计算类似,这里就不赘述了,代码实现如下:

//左孩子节点数
int BinaryTreeLLeafSize(BTNode* root)
{if (root == NULL)return 0;if (root->left != NULL)return BinaryTreeLLeafSize(root->left) + BinaryTreeLLeafSize(root->right)+1;elsereturn BinaryTreeLLeafSize(root->left) + BinaryTreeLLeafSize(root->right);
}

12.实现链式二叉树右孩子节点数计算

右孩子结点的计算上我们同样采用递归分治的思想,即

根结点的右孩子节点数=左子树的右孩子节点数+右子树的右孩子节点数.

当然,右孩子结点的判断条件根结点存在且右子树不为空.

该函数的递归展开和叶子节点计算类似,这里就不赘述了,代码实现如下:

//右孩子节点数
int BinaryTreeRLeafSize(BTNode* root)
{if (root == NULL)return 0;if (root->right != NULL)return BinaryTreeRLeafSize(root->left) + BinaryTreeRLeafSize(root->right)+1;elsereturn BinaryTreeRLeafSize(root->left) + BinaryTreeRLeafSize(root->right);
}

13.实现链式二叉树节点数计算

树的结点的计算上我们同样采用递归分治的思想,即

根结点的节点数=左子树的节点数+右子树的节点数.

当然,结点的判断标条件根结点存在.

该函数的递归展开和叶子节点计算类似,这里就不赘述了,代码实现如下:

//树的结点个数
int TreeSize(BTNode* root)
{if (root == NULL)return 0;elsereturn TreeSize(root->left) + TreeSize(root->right) + 1;//返回左子树个数和右子树个数加上自己//return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;//从这条语句里体会分治的思想
}

14.实现链式二叉树高度计算

二叉树的高度计算上我们同样采用递归分治的思想,即

二叉树的高度左子树和右子树高的那个高度加上根结点自己的高度,即

二叉树的高度=左子树的高度>右子树的高度?左子树高度+1:右子树高度+1.

函数的递归展开图如下:

该部分代码实现如下:

//树高
int TreeHeight(BTNode* root)
{if (root == NULL)return 0;int leftHeight = TreeHeight(root->left);int rightHeight = TreeHeight(root->right);return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1 ;//返回左子树和右子树高的那个并加上自己那份
}

15.实现链式二叉树查询某层结点个数

某层结点的计算我们同样采用递归分治的思想,即

根结点的K层节点数=左子树的K层节点数+右子树的K层节点数.

当然,K层结点的判断条件在K层且结点存在.

该函数的递归展开和高度计算类似,这里就不赘述了,代码实现如下:

//层结点个数
int TreeKLevel(BTNode* root, int k)
{assert(k > 0);if (root == NULL)    //思路是,对每个根结点分别求左子树第k层的结点+右子树第k层的结点return 0;//如果在k层,且不为空,则就是k层的有效结点if (k == 1)return 1;return TreeKLevel(root->left, k - 1) + TreeKLevel(root->right, k - 1);
}

16.实现链式二叉树查询某节点是否存在

查询某节点是否存在我们同样采用递归分治的思想,即

树中是否存在该结点取决于左子树是否存在该节点右子树是否存在该节点,

树中是否存在该结点 = 左子树是否存在该节点 || 右子树是否存在该节点

我们采取后序遍历的思想,从叶子节点逐级上传查找结果,如果没找到,就逐级传上空结点,如果找到了,就将该节点逐级传到根结点.

只要左右子树其中有一个存在这个结点,那树中就是存在该结点的.

该部分代码实现如下:

//二叉树查找值为x的结点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{if (root == NULL)return NULL;if (root->data == x)return root;//找到了就逐级传回BTNode* lret = BinaryTreeFind(root->left, x);if (lret){return lret;}BTNode* rret = BinaryTreeFind(root->right, x);if (rret){return rret;}return NULL;
}

17.实现链式二叉树判断是否为完全二叉树

我们判断二叉树是否为完全二叉树的思路是利用完全二叉树的性质:

完全二叉树不为满二叉树,空节点必定连续出现在最后一层的靠右部分.

如下图所示,虚线所表示的是空结点:

因此我们利用层序遍历的思路,将所有结点的空孩子也入队,当完全二叉树遍历到第一个空结点时后面一定全为空结点,如果后面还有非空结点,那么这颗树就不是完全二叉树.

该部分代码实现如下(实现链队列部分在文末的Queue文件中):

//判断一棵树是否是完全二叉树
bool TreeComplete(BTNode* root)
{//完全二叉树按层序走,非空结点一定是连续的(出过的结点的空子树也被无形中带入队了,不用担心结点在后面没有入队)Que q;QueueInit(&q);if (root)QueuePush(&q, root);while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);if (front==NULL){break;}else{QueuePush(&q, front->left);QueuePush(&q, front->right);}}//判断是不是完全二叉树(即出队过程中剩余元素有没有非空的结点)while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);//front不为空,就为真,就返回假if (front){QueueDestroy(&q);return false;}}QueueDestroy(&q);return true;
}

18.实现翻转链式二叉树

翻转二叉树我们同样采用递归分治的思想,即

翻转二叉树=翻转左子树+翻转右子树.

我们采用后序遍历的思想,最后再翻转根节点的左右子树

该部分代码实现如下:

//翻转二叉树
BTNode* InvertTree(BTNode* root)
{if (root == NULL)return NULL;BTNode* tmp = InvertTree(root->right);root->right = InvertTree(root->left);root->left = tmp;return root;
}

19.实现链式二叉树的销毁

二叉树的销毁我们采用后序递归遍历的思想,即:先销毁左右子树,再销毁根节点

该部分代码实现如下:

//销毁树
void TreeDestory(BTNode* root)
{if (root == NULL)return;//递归后序销毁TreeDestory(root->left);TreeDestory(root->right);free(root);
}

四.项目完整代码

我们将程序运行的代码分别在三个工程文件中编辑,完整代码如下:

BTree.c文件

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
#include"Queue.h"typedef char BTDataType;typedef struct BinaryTreeNode
{BTDataType data;struct BinarytreeNode* left;struct BinarytreeNode* right;
}BTNode;//获取二叉树结点
BTNode* BuyNode(BTDataType x)
{BTNode* node = (BTNode*)malloc(sizeof(BTNode));if (node == NULL){perror("malloc fail::");return NULL;}node->data = x;node->left = NULL;node->right = NULL;return node;
}//前序建树
BTNode* CreatTree(char* a, int* pi)
{if (a[(*pi)] == '#')  //不是#不++!{(*pi)++;return NULL;}BTNode* root = BuyNode(a[(*pi)]);(*pi)++;root->left = CreatTree(a, pi);root->right = CreatTree(a, pi);return root;
}//前序遍历
void PreOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}printf("%c ", root->data);PreOrder(root->left);PreOrder(root->right);
}//中序遍历
void InOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}InOrder(root->left);printf("%c ", root->data);InOrder(root->right);
}//后序遍历
void PostOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}PostOrder(root->left);PostOrder(root->right);printf("%c ", root->data);
}//树的结点个数
int TreeSize(BTNode* root)
{if (root == NULL)return 0;elsereturn TreeSize(root->left) + TreeSize(root->right) + 1;//返回左子树个数和右子树个数加上自己//return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;//从这条语句里体会分治的思想
}//树高
int TreeHeight(BTNode* root)
{if (root == NULL)return 0;int leftHeight = TreeHeight(root->left);int rightHeight = TreeHeight(root->right);return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1 ;//返回左子树和右子树高的那个并加上自己那份
}//层结点个数
int TreeKLevel(BTNode* root, int k)
{assert(k > 0);if (root == NULL)    //思路是,对每个根结点分别求左子树第k层的结点+右子树第k层的结点return 0;//如果在k层,且不为空,则就是k层的有效结点if (k == 1)return 1;return TreeKLevel(root->left, k - 1) + TreeKLevel(root->right, k - 1);
}//二叉树查找值为x的结点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{if (root == NULL)return NULL;if (root->data == x)return root;//找到了就逐级传回BTNode* lret = BinaryTreeFind(root->left, x);if (lret){return lret;}BTNode* rret = BinaryTreeFind(root->right, x);if (rret){return rret;}return NULL;
}//层序遍历(借助队列)
void LevelOrder(BTNode* root)
{Que q;QueueInit(&q);if (root)QueuePush(&q, root);while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);printf("%c ", front->data);if (front->left != NULL){QueuePush(&q, front->left);}if (front->right != NULL){QueuePush(&q, front->right);}}QueueDestroy(&q);
}//判断一棵树是否是完全二叉树
bool TreeComplete(BTNode* root)
{//完全二叉树按层序走,非空结点一定是连续的(出过的结点的空子树也被无形中带入队了,不用担心结点在后面没有入队)Que q;QueueInit(&q);if (root)QueuePush(&q, root);while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);if (front==NULL){break;}else{QueuePush(&q, front->left);QueuePush(&q, front->right);}}//判断是不是完全二叉树(即出队过程中剩余元素有没有非空的结点)while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);//front不为空,就为真,就返回假if (front){QueueDestroy(&q);return false;}}QueueDestroy(&q);return true;
}//销毁树
void TreeDestory(BTNode* root)
{if (root == NULL)return;//递归后序销毁TreeDestory(root->left);TreeDestory(root->right);free(root);
}//叶子节点数
int BinaryTreeLeafSize(BTNode* root)
{if (root == NULL)return 0;if (root->left==NULL && root->right==NULL)//左右子树都为空就是叶子结点{return 1;}return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);//返回每颗子树的左右叶子节点数
}//左孩子节点数
int BinaryTreeLLeafSize(BTNode* root)
{if (root == NULL)return 0;if (root->left != NULL)return BinaryTreeLLeafSize(root->left) + BinaryTreeLLeafSize(root->right)+1;elsereturn BinaryTreeLLeafSize(root->left) + BinaryTreeLLeafSize(root->right);
}//右孩子节点数
int BinaryTreeRLeafSize(BTNode* root)
{if (root == NULL)return 0;if (root->right != NULL)return BinaryTreeRLeafSize(root->left) + BinaryTreeRLeafSize(root->right)+1;elsereturn BinaryTreeRLeafSize(root->left) + BinaryTreeRLeafSize(root->right);
}//判断树空
bool BTEmpty(BTNode* root)
{return (!root);
}//翻转二叉树
BTNode* InvertTree(BTNode* root)
{if (root == NULL)return NULL;BTNode* tmp = InvertTree(root->right);root->right = InvertTree(root->left);root->left = tmp;return root;
}//菜单二叉树
void BTMenu()
{printf("****************************************\n");printf("******请选择要进行的操作          ******\n");printf("******1.先序建树                  ******\n");printf("******2.查询树是否为空            ******\n");printf("******3.树的先序遍历              ******\n");printf("******4.树的中序遍历              ******\n");printf("******5.树的后序遍历              ******\n");printf("******6.树的层序遍历              ******\n");printf("******7.树的叶子节点数            ******\n");printf("******8.树的左孩子节点个数        ******\n");printf("******9.树的右孩子节点个数        ******\n");printf("******10.树的节点个数             ******\n");printf("******11.树的高度                 ******\n");printf("******12.查询树某层的节点个数     ******\n");printf("******13.查询结点是否在树中       ******\n");printf("******14.查询树是否是完全二叉树   ******\n");printf("******15.翻转此树                 ******\n");printf("******0.退出二叉树程序            ******\n");printf("****************************************\n");printf("请选择:>");
}int main()
{BTNode* root = NULL;int swi = 0;do         {BTMenu();scanf("%d", &swi);switch (swi){case 0:TreeDestory(root); // 释放树内存printf("您已退出程序:>\n");break;case 1:printf("请输入树:>(以'#'表示空孩子)\n");char a[100];scanf("%s", a);int i = 0;root = CreatTree(a, &i);printf("已成功建树:>\n");break;case 2:if (BTEmpty(root) == true) //判断树空printf("当前树为空\n");elseprintf("当前树不为空\n");break;case 3:printf("先序遍历此树: ");    PreOrder(root);printf("\n");break;case 4:printf("中序遍历此树: ");InOrder(root);printf("\n");break;case 5:printf("后序遍历此树: ");PostOrder(root);printf("\n");break;case 6:printf("层序遍历此树: ");LevelOrder(root);printf("\n");break;case 7:printf("此树的叶子节点数有: %d\n", BinaryTreeLeafSize(root));break;case 8:printf("此树的左孩子节点数有: %d\n", BinaryTreeLLeafSize(root));break;case 9:printf("此树的右孩子节点数有: %d\n", BinaryTreeRLeafSize(root));break;case 10:printf("此树的节点数有:%d\n", TreeSize(root));break;case 11:printf("此树的高度为:%d\n", TreeHeight(root));break;case 12:printf("请输入你要查询的层数:>");int k = 0;scanf("%d", &k);printf("此树第%d层的节点数有:%d\n",k,TreeKLevel(root,k));break;case 13:printf("请输入你要查询的结点:>");setbuf(stdin, NULL);//之前键盘缓冲区有污染,该函数作用是清空键盘缓冲区BTDataType x = 0;scanf("%c", &x);//二叉树查找值为x的结点if (BinaryTreeFind(root, x) != NULL){printf("结点%c在树中:>\n",x);}else{printf("结点%c不在树中:<\n",x);}break;case 14:if (TreeComplete(root)){printf("该树是完全二叉树:>\n");}else{printf("该树不是完全二叉树:<\n");}break;case 15:root = InvertTree(root);printf("翻转成功:>\n");printf("前序遍历结果为:");PreOrder(root);printf("\n");break;default:printf("输入错误,请重新输入\n");break;}} while (swi);return 0;
}

 Queue.c 文件

#include"Queue.h"void QueueInit(Que* pq)
{assert(pq);pq->head = pq->tail = NULL;pq->size = 0;}
void QueueDestroy(Que* pq)
{assert(pq);QNode* cur = pq->head;while (cur){QNode* next = cur->next;free(cur);cur = next;}pq->head = pq->tail = NULL;pq->size = 0;}void QueuePush(Que* pq, QDatatype x) //入队是尾插
{QNode* newnode = (QNode*)malloc(sizeof(QNode));if (newnode == NULL){perror("malloc fail");return;}newnode->data = x;newnode->next = NULL;if (pq->head == NULL)//头插{assert(pq->tail == NULL);//head为空,tail不为空就出事了,队头为空但是队尾不为空pq->head = pq->tail = newnode;}else//尾插{pq->tail->next = newnode;pq->tail = newnode;}pq->size++;}void QueuePop(Que* pq)//出队是头删
{assert(pq);assert(!QueueEmpty(pq));//assert为假会终止程序QNode* cur = pq;if (pq->head->next == NULL){free(pq->head);pq->head = pq->tail = NULL;}else{QNode* next = pq->head->next;free(pq->head);pq->head = next;}pq->size--;}int QueueSize(Que* pq)
{assert(pq);return pq->size;}bool QueueEmpty(Que* pq)//判空!为空返回真!
{assert(pq);return pq->size==0;
}QDatatype QueueFront(Que* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->head->data;}
QDatatype QueueBack(Que* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->tail->data;}void QMenu()
{printf("**********************************\n");printf("******请选择要进行的操作    ******\n");printf("******1.链队列入队          ******\n");printf("******2.链队列出队          ******\n");printf("******3.取队首元素          ******\n");printf("******4.取队尾元素          ******\n");printf("******5.队列判空            ******\n");printf("******6.查询当前队列长      ******\n");printf("******7.清空队列            ******\n");printf("******0.退出链队列程序      ******\n");printf("**********************************\n");printf("请选择:>");}

Queue.h文件

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>typedef struct BinaryTreeNode* QDatatype;typedef struct QueueNode//一个是结点结构
{struct QueueNode* next;QDatatype data;
}QNode;typedef struct Queue//一个是队列整体结构
{QNode* head;QNode* tail;int size ;
}Que;void QueueInit(Que* pq);
void QueueDestroy(Que* pq);void QueuePush(Que* pq,QDatatype x);
void QueuePop(Que* pq);int QueueSize(Que* pq);bool QueueEmpty(Que* pq);QDatatype QueueFront(Que* pq);
QDatatype QueueBack(Que* pq);void QMenu();

结语

希望这篇链式二叉树的实现详解能对大家有所帮助,欢迎大佬们留言或私信与我交流.

学海漫浩浩,我亦苦作舟!关注我,大家一起学习,一起进步!

相关文章推荐

【数据结构】C语言实现顺序表万字详解(附完整运行代码)

【数据结构】C语言实现单链表万字详解(附完整运行代码)

【数据结构】C语言实现带头双向循环链表万字详解(附完整运行代码)

【数据结构】用C语言实现顺序栈(附完整运行代码)

【数据结构】C语言实现链队列(附完整运行代码)


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/577404.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode-回文链表(234)

题目描述&#xff1a; 给你一个单链表的头节点 head &#xff0c;请你判断该链表是否为回文链表。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 因为这一题是受到876题求链表中间节点的启发&#xff0c;所以在这里也加一下。 876.链表的中间结点…

探索 HTTP 请求的世界:get 和 post 的奥秘(下)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

在Go语言中实现HTTP中间件

在Web开发中&#xff0c;中间件是一种非常流行的设计模式&#xff0c;它可以用于处理请求、拦截请求并对其进行处理或转换。而在Go语言中&#xff0c;实现HTTP中间件就像给自己的HTTP服务器穿上了一层“魔法外衣”&#xff0c;让它变得更加灵活、可配置和可扩展。下面&#xff…

有可以平替SSL证书的数字证书吗

SSL证书和数字证书并不是完全等价的概念。SSL证书是一种数字证书的一种应用&#xff0c;用于在网络中加密和身份验证。数字证书是一种用于加密通信和验证身份的技术&#xff0c;SSL证书就是数字证书的一种特定形式。 除了SSL证书之外&#xff0c;还有其他类型的数字证书&#…

QT trimmed和simplified

trimmed&#xff1a;去除了字符串开头前和结尾后的空白&#xff1b; simplified&#xff1a;去除了字符串开头前和结尾后的空白&#xff0c;以及中间内部的空白字符也去掉&#xff08;\t,\n,\v,\f,\r和 &#xff09; 代码&#xff1a; QString str " 1 2 3 4 5 …

Apache Commons BeanUtils: JavaBean操作的艺术

第1部分&#xff1a;Apache Commons BeanUtils 简介 咱们今天聊聊Apache Commons BeanUtils。这货简直就是处理JavaBean的利器&#xff0c;用起来又方便又快捷。不管是属性拷贝、类型转换&#xff0c;还是动态访问&#xff0c;BeanUtils都能轻松应对。 BeanUtils是啥&#xf…

043、循环神经网络

之——RNN基础 杂谈 第一个对于序列模型的网络&#xff0c;RNN。 正文 1.潜变量自回归模型 潜变量总结过去的信息&#xff0c;再和当前信息一起结合出新的信息。 2.RNN 循环神经网络将观察作为x&#xff0c;与前层隐变量结合得到输出 其中Whh蕴含了整个模型的时序信息&#xf…

NET中使用SQLSugar操作sqlserver数据库

目录 一、SqlSugar是什么&#xff1f; 二、迁移和建表 1.建立实体 2.创建上下文类 3.在Program中添加SqlSugar服务 4.在控制器中注入上下文类 三、简单实现CURD功能 总结 一、SqlSugar是什么&#xff1f; SqlSugar是一款老牌 .NET 开源ORM框架。 主要特点&#xff1a…

让某个页面一直处于最前面,可以屏蔽切屏检测

前言 学习通智慧树网课分屏&#xff0c;让某个页面一直处于最前面&#xff0c;可以屏蔽切屏检测。 页面一直处于最前面 前言1 安装包2 使用 1 安装包 https://download.csdn.net/download/qq_44850489/76684366 2 使用 一直下一步就可以 选择要放到前面的窗口&#xff0c…

设计模式--工厂方法模式

实验3&#xff1a;工厂方法模式 本次实验属于模仿型实验&#xff0c;通过本次实验学生将掌握以下内容&#xff1a; 1、理解工厂方法模式的动机&#xff0c;掌握该模式的结构&#xff1b; 2、能够利用工厂方法模式解决实际问题。 [实验任务]&#xff1a;加密算法 目前常用…

ts相关笔记(extends、infer、Pick、Omit)

最近刷了本ts小册&#xff0c;对一些知识点做下笔记。 extends extends 是一个关键字&#xff0c;用于对类型参数做一些约束。 A extends B 意味着 A 是 B 的子类型&#xff0c;比如下面是成立的 ‘abc’ extends string599 extends number 看下面例子&#xff1a; type …

Leetcode—86.分隔链表【中等】

2023每日刷题&#xff08;六十九&#xff09; Leetcode—86.分隔链表 实现代码 /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/ struct ListNode* partition(struct ListNode* head, int x) {struct ListNode…

C语言操作符if语句好习惯 详解分析操作符(详解4)

各位少年&#xff1a; 前言 还记得我们上一章讲过一个比较抽象的代码&#xff0c;它要比较两次都是真的情况下才能打印&#xff0c;那么很显然这样写代码是有弊端的&#xff1f;哪我们C语言之父丹尼斯.里奇&#xff0c;先介绍一下上次拉掉了if语句的好习惯 好再分享一些操作符…

【基础篇】一、认识JVM

文章目录 1、虚拟机2、Java虚拟机3、JVM的整体结构4、Java代码的执行流程5、JVM的三大功能6、JVM的分类7、JVM的生命周期 1、虚拟机 虚拟机&#xff0c;Virtual Machine&#xff0c;一台虚拟的计算机&#xff0c;用来执行虚拟计算机指令。分为&#xff1a; 系统虚拟机&#x…

结构体的对齐规则

1.引入 我们在掌握了结构体的基本使⽤后。 现在我们深⼊讨论⼀个问题&#xff1a;计算结构体的大小。 这也是⼀个特别热门的考点&#xff1a; 结构体内存对齐。 2.具体分析 ⾸先我们得掌握结构体的对⻬规则&#xff1a; 1. 结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量…

Flutter详解及案例代码

概念 Flutter是由Google开发的开源UI框架&#xff0c;旨在快速构建高质量的移动应用程序。与传统的移动应用开发方式不同&#xff0c;Flutter使用单一代码库构建应用程序&#xff0c;可以同时在iOS和Android上运行。 Flutter的核心是使用Dart语言编写的&#xff0c;并且具有自…

如何解决HTTP 404错误,这里给出详细解决办法

404错误是一个HTTP状态代码,这意味着你试图在网站上访问的页面在他们的服务器上找不到。 需要明确的是,该错误表示虽然服务器本身是可访问的,但显示该错误的特定页面是不可访问的。 个别网站经常自定义这个错误信息。所以,请记住,错误可能会以任何可以想象的方式出现,这…

网络编程--socket编程

这里写目录标题 套接字概念通信原理总结 预备知识网络字节序简介字节转换函数 IP地址转换函数为什么单独列出函数原型sockaddr结构体 一级目录二级目录二级目录二级目录 一级目录二级目录二级目录二级目录 套接字 概念 Socket本身有插座的意思&#xff0c;但他是进程之间网络通…

解决Qt“报无法定位程序输入点xxx于动态连接库“问题

今天&#xff0c;在使用QtVS2019编译工程时&#xff0c;弹出"无法定位程序输入点xxx于动态链接库"问题&#xff0c;如图(1)所示&#xff1a; 图(1) 报"无法定位程序输入点xxx于动态链接库"问题 出现这种问题的原因有很多&#xff1a; (1) 工程Release/Deb…

Android UID相关知识

一、UID/PID/GID/GIDS的含义和作用 UID : android中uid用于标识一个应用程序&#xff0c;uid在应用安装时被分配&#xff0c;并且在应用存在于手机上期间&#xff0c;都不会改变。一个应用程序只能有一个uid&#xff0c;多个应用可以使用sharedUserId 方式共享同一个uid&#…