一、自定义数据结构
#define _CRT_SECURE_NO_WARNINGS
// Exp4-2_BiTree_FrequentlyExam01
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define BI_TREE_NODE_MAX 64
// 二叉树结点的定义
typedef struct _BiNode
{
char data;
struct _BiNode* lchild;
struct _BiNode* rchild;
}BiNode, * BiTree; // BiTree 完全等价于 BiNode *
二、根据二叉树的完整先序序列,递归创建二叉树的二叉链表存储结构
二叉树各种遍历功能的实现(c/c++)_我龙傲天誓死守护刘波儿的博客-CSDN博客(1)
和(1)的“由完整先序序列递归创建二叉树”不同的是,(1)是在文件中读取完整先序序列,本文是利用引用变量i从数组中读取完整先序序列。
int createBiTreeV1(char completePre[], BiTree& root, int& i)
{
// static int i = 0; // 在递归函数中,不要轻易使用static变量
char ch;
BiNode* p;
// 从完整的先序序列中,依次获取各个结点的值(空树为#)
ch = completePre[i++];
if ('#' != ch)
{
// 开辟空间,构造结点,最后递归创建左右子树
p = (BiNode*)malloc(sizeof(BiNode));
p->data = ch;
root = p; // 将新结点的地址p返回,作为当前二叉树的根结点
// 递归创建ch结点的左子树
int isLeftSuccess = createBiTreeV1(completePre, p->lchild, i);
// 递归创建ch结点的右子树
int isRightSuccess = createBiTreeV1(completePre, p->rchild, i);
if (isLeftSuccess == 1 && isRightSuccess == 1)
return 1;
else
return 0;
}
else
{
root = NULL; // 当前二叉树为空,将其根结点的指针设为NULL
return 1;
}
}
三、根据二叉树的先序和中序序列,递归创建二叉树的二叉链表存储结构
int createBiTreeV2(char pre[], int preStart, int preEnd,
char in[], int inStart, int inEnd, BiNode*& root)
{
char ch;
int i;
BiNode* p;
// 取到二叉树的根结点,即,先序序列中的第一个结点
if (preStart <= preEnd)
{
ch = pre[preStart];
}
else // 如果当前先序序列已不存在,这说明该二叉树为空
{
root = NULL;
return 1;
}
// 从中序序列中找到根结点ch,用i记录其在中序数组中的下标
for (i = inStart; i <= inEnd; i++)
{
if (in[i] == ch)
break;
}
if (i <= inEnd) // 如果中序序列中找到了根结点
{
int leftNodeNum = i - inStart; // 左子树的结点数
int rightNodeNum = inEnd - i; // 右子树的结点数
// 开辟空间,构造结点,最后递归创建左右子树
p = (BiNode*)malloc(sizeof(BiNode));
p->data = ch;
// 将p所指结点设置为根
root = p;
// 递归创建左子树
int isLeftSuccess = createBiTreeV2(pre, preStart + 1, preStart + 1 + leftNodeNum - 1,
in, inStart, inStart + leftNodeNum - 1, p->lchild);
int isRightSuccess = createBiTreeV2(pre, preStart + leftNodeNum + 1, preStart + leftNodeNum + 1 + rightNodeNum - 1,
in, i + 1, i + 1 + rightNodeNum - 1, p->rchild);
if (isLeftSuccess == 1 && isRightSuccess == 1)
return 1;
else
return 0;
}
else // 当前先序或中序序列有问题,二叉树创建失败
return 0;
}
四、递归遍历二叉树
// 访问结点函数
void visit(BiTree root) {
printf("%2c", root->data);
}
// 递归方式先序遍历
void preOrder(BiTree root)
{
if (root) {
visit(root);
preOrder(root->lchild);
preOrder(root->rchild);
}
}
// 递归方式中序遍历
void inOrder(BiTree root)
{
if (root) {
inOrder(root->lchild);
visit(root);
inOrder(root->rchild);
}
}
// 递归方式后序遍历
void postOrder(BiTree root)
{
if (root) {
inOrder(root->lchild);
inOrder(root->rchild);
visit(root);
}
}
五、获得叶子结点数
int getNumOfLeaf(BiTree root)
{
/*正确但不建议用
static int count = 0; //递归函数里最好不要用全局变量
if (root) {
if (root->lchild == NULL && root->rchild == NULL) {
count++;
}
getNumOfLeaf(root->lchild);
getNumOfLeaf(root->rchild);
}
return count;*/
if (root == NULL)
return 0;
if (root->lchild == NULL && root->rchild == NULL)
return 1;
return getNumOfLeaf(root->lchild) + getNumOfLeaf(root->rchild);
}
六、获取度为1的结点数
int getNumOfDegreeOne(BiTree root)
{
/*可行但不建议
static int count = 0;
if (root) {
if ((root->lchild == NULL && root->rchild != NULL) || (root->lchild != NULL && root->rchild == NULL)) {
count++;
}
getNumOfDegreeOne(root->lchild);
getNumOfDegreeOne(root->rchild);
return count;
}
return 0;*/
if (root == NULL)
return 0;
if ((root->lchild == NULL && root->rchild != NULL)
||( root->lchild != NULL && root->rchild == NULL))
return 1 + getNumOfDegreeOne(root->lchild) + getNumOfDegreeOne(root->rchild);
return getNumOfDegreeOne(root->lchild) + getNumOfDegreeOne(root->rchild);
}
七、获取度为2的结点数
int getNumOfDegreeTwo(BiTree root)
{
if (root == NULL)
return 0;
if (root->lchild != NULL && root->rchild != NULL)
return 1 + getNumOfDegreeTwo(root->lchild) + getNumOfDegreeTwo(root->rchild);
return getNumOfDegreeTwo(root->lchild) + getNumOfDegreeTwo(root->rchild);
// 法二:利用n0 = n2 + 1
// return getNumOfLeaf(root)-1;
}
八、获取总结点数
int getTotalNumOfNodes(BiTree root)
{
if (root == NULL)
return 0;
return 1 + getTotalNumOfNodes(root->lchild) + getTotalNumOfNodes(root->rchild);
}
九、求二叉树的深度/高度
int getHeight(BiTree root)
{
if (root == NULL) {
return 0;
}
else {
int l = getHeight(root->lchild);
int r = getHeight(root->rchild);
return l > r ? l + 1 : r + 1;
}
}
十、查找二叉树中的指定结点x,返回该结点的地址,若不存在,则返回NULL
BiNode* getSpecifiedNode(BiTree root, char x)
{
if (root == NULL)
return NULL;
if (root->data == x)
return root;
// 在左子树查找
BiNode* l = getSpecifiedNode(root->lchild, x);
if (l != NULL) return l;
//在右子树查找
BiNode* r = getSpecifiedNode(root->rchild, x);
return r;
}
十一、获取二叉树中指定结点x所在的层次,若二叉树中不存在x,则返回-1
int getLevelOfNode(BiTree root, char x)
{
if (root == NULL)
return -1;
if (root->data == x)
return 1;
// 在左子树查找
int l = getLevelOfNode(root->lchild, x);
if (l != -1) return l+1;
//在右子树查找
int r = getLevelOfNode(root->rchild, x);
if (r != -1) return r+1;
return -1;
}
十二、翻转二叉树,即交换所有结点的左右子树
// 遍历每一个结点然后交换其左右孩子
void swapChild(BiTree root)
{
if (root) {
BiNode* t;
t = root->lchild;
root->lchild = root->rchild;
root->rchild = t;
swapChild(root->lchild);
swapChild(root->rchild);
}
}
十三、获取指定层次的所有叶子结点
每递归到一个子树,所求层次在子树中就要-1,知道递归至新的root在所所求层次(level=1)上,再判断其是否为叶子结点(root->lchild == NULL && root->rchild == NULL),若是则用leaves[]保存,若不是则继续递进,直到root为空进行回归。
void getNumOfLeafOnSpecifiedLevel(BiTree root, int level, char leaves[], int& i)
{
// static int i = 0; // 在递归函数中,不要轻易使用static变量
if (root == NULL)
return;
if (level == 1 && (root->lchild == NULL && root->rchild == NULL)) //递归出口
leaves[i++] = root->data;
else
{
getNumOfLeafOnSpecifiedLevel(root->lchild, level - 1, leaves, i);
getNumOfLeafOnSpecifiedLevel(root->rchild, level - 1, leaves, i);
}
}
十四、判断两棵二叉树是否相等(形状一样,并且所有对应结点的值都相等),相等返回1,不等返回0
小tips:特殊情况优先考虑。
int isSame(BiTree root1, BiTree root2)
{
if (root1 == NULL && root2 == NULL)
return 1;
if (!root1 || !root2) // 其中一个为空
return 0;
if (root1->data == root2->data) // 如果根结点相等,判断其左右子树是否相等
return isSame(root1->lchild, root2->lchild) && isSame(root1->rchild, root2->rchild);
else
return 0;
}
十五、判断两棵二叉树是否相似(形状一样,但各结点的值并不完全相同)
int isSimilar(BiTree root1, BiTree root2)
{
if (root1 == NULL && root2 == NULL)
return 1;
if (!root1 || !root2) // 其中一个为空
return 0;
return isSimilar(root1->lchild, root2->lchild) && isSimilar(root1->rchild, root2->rchild);
return 0;
}
十六、递归方式销毁二叉树
void destroy(BiTree root)
{
if (NULL != root)
{
if (NULL != root->lchild) // 左子树不为空,则递归销毁左子树
destroy(root->lchild);
if (NULL != root->rchild) // 右子树不为空,则递归销毁右子树
destroy(root->rchild);
printf("Node %c has been freed!\n", root->data);
free(root); // 最后直接释放根结点
}
}
十七、功能菜单和主函数
// 功能菜单
void menu()
{
printf("\n\t************************Binary Linked List************************\n");
printf("\t* 1--Read data from a file to create a binary tree *\n");
printf("\t* 2--PreOrder InOrder and PostOrder traversal by recursion *\n");
printf("\t* 3--Get the number of all kinds of nodes *\n");
printf("\t* 4--Get the depth or height of the BiTree *\n");
printf("\t* 5--Get the specified node(left and right children, level) *\n");
printf("\t* 6--Swap all left and right subtrees *\n");
printf("\t* 7--Get the number of leaf nodes on specified level *\n");
printf("\t* 8--Judge if two trees are same *\n");
printf("\t* 9--Judge if two trees are similar *\n");
printf("\t* 10--Destroy the entire binary tree *\n");
printf("\t* 11--Clear screen *\n");
printf("\t* 0--Exit program *\n");
printf("\t******************************************************************\n");
printf("\tPlease select a menu item:");
}
int main()
{
int choice;
char c;
FILE* fpFrom;
BiTree root = NULL; // BiTree 完全等价于 BiNode *
system("chcp 65001"); // 设置window控制台(CMD或命令行窗口)为UTF-8格式
while (1)
{
menu();
scanf("%d", &choice);
switch (choice)
{
case 1:
if (NULL != root)
printf("The current binary tree is not empty, please destroy it before rebuild!\n");
else
{
// 因为createBiTree()函数递归时要不断从文件中读取字符,所以先打开文件
// 如果在createBiTree()函数中每读一个字符,就打开并关闭文件一次,则效率太低
fpFrom = fopen("BT.dat", "r");
if (fpFrom == NULL)
{
printf("File BT.dat containing PreOrdered and InOrdered sequence cannot be opened, binary tree creation failed!\n");
break;
}
// 二叉树的先序和中序序列
char pre[BI_TREE_NODE_MAX], in[BI_TREE_NODE_MAX];
fscanf(fpFrom, "%s", pre); // 先读先序序列
fscanf(fpFrom, "%s", in); // 再读中序序列
fclose(fpFrom);
int nodeNum = strlen(pre); // 得到二叉树中的结点数
// 通过先序和中序序列,恢复二叉树的二叉链表存储结构
int ret = createBiTreeV2(pre, 0, nodeNum - 1, in, 0, nodeNum - 1, root);
if (ret == 1)
printf("Binary tree created successfully!\n");
else
printf("Binary tree created failed!\n");
}
break;
case 2:
if (NULL != root)
{
printf("The PreOrdered sequence is as follows:");
preOrder(root);
printf("\nThe InOrdered sequence is as follows:");
inOrder(root);
printf("\nThe PostOrdered sequence is as follows:");
postOrder(root);
printf("\n");
}
else
printf("The current binary tree is empty, please create the binary tree first!\n");
break;
case 3: // 获得各种结点的数量(度为0,度为1,度为2,总结点数)
if (NULL != root)
{
printf("The number of all kinds of nodes is as follows:\n");
int leaf = getNumOfLeaf(root); // 获得度为0的结点数
int degreeOne = getNumOfDegreeOne(root); // 获得度为1的结点数
int degreeTwo = getNumOfDegreeTwo(root); // 获得度为2的结点数
int total = getTotalNumOfNodes(root); // 获得结点的总数
printf("Number of leaf = %d\n", leaf);
printf("Number of degree one = %d\n", degreeOne);
printf("Number of degree two = %d\n", degreeTwo);
printf("Number of all nodes = %d\n", total);
printf("\n");
}
else
printf("The current binary tree is empty, please create the binary tree first!\n");
break;
case 4:
if (NULL != root)
{
int height = getHeight(root); // 获得二叉树的深度或高度
printf("The depth or height of the BiTree is %d.\n", height);
printf("\n");
}
else
printf("The current binary tree is empty, please create the binary tree first!\n");
break;
case 5:
if (NULL != root)
{
printf("Please enter the value of the node you are looking for:\n");
// the buffer needs to be emptied
char x;
while ((x = getchar()) != '\n' && x != EOF);
scanf("%c", &x);
BiNode* p = getSpecifiedNode(root, x); // 查找二叉树中的指定结点
if (p == NULL)
{
printf("There is not exist node %c in the binary tree. \n", x);
}
else
{
if (p->lchild != NULL)
{
printf("The left child of %c is %c. \n", x, p->lchild->data);
}
else
{
printf("The left child of %c is not exist. \n", x);
}
if (p->rchild != NULL)
{
printf("The right child of %c is %c. \n", x, p->rchild->data);
}
else
{
printf("The right child of %c is not exist. \n", x);
}
}
int level = getLevelOfNode(root, x); // 获取二叉树中指定结点所在的层次
if (level != -1)
{
printf("The level of %c is %d. \n", x, level);
}
printf("\n");
}
else
printf("The current binary tree is empty, please create the binary tree first!\n");
break;
case 6:
if (NULL != root)
{
swapChild(root); // 翻转二叉树,交换所有结点的左右子树
printf("The binary tree was flipped successfully!\n");
printf("\n");
}
else
printf("The current binary tree is empty, please create the binary tree first!\n");
break;
case 7:
if (NULL != root)
{
char leaves[BI_TREE_NODE_MAX], c;
int leavesNum, level;
printf("Please enter the level:\n");
scanf("%d", &level);
// 防止用户输入的不是整数,清空一下缓冲区
while ((c = getchar()) != '\n' && c != EOF);
leavesNum = 0;
getNumOfLeafOnSpecifiedLevel(root, level, leaves, leavesNum); // 获得指定层次的所有叶子结点
if (leavesNum > 0)
{
printf("All the leaves at level %d are as follows:\n", level);
for (int i = 0; i < leavesNum; i++)
{
printf("%3c", leaves[i]);
}
printf("\n");
}
printf("The level %d have %d leaf nodes.\n", level, leavesNum);
printf("\n");
}
else
printf("The current binary tree is empty, please create the binary tree first!\n");
break;
case 8: // 判断两棵二叉树是否相等
{ // 此对大括号相当于构造一个命名空间,不要去掉
if (root == NULL)
{
printf("The current binary tree(BT.dat) is empty, you may need to create it first!\n");
}
BiTree root2 = NULL;
FILE* fpFrom = fopen("BT2.dat", "r");
if (fpFrom == NULL)
{
printf("File BT2.dat containing the complete PreOrdered sequence cannot be opened, binary tree creation failed!\n");
break;
}
// 读取二叉树的完整先序序列
char completePre[BI_TREE_NODE_MAX * 2];
fscanf(fpFrom, "%s", completePre); // 读取二叉树的完整先序序列
fclose(fpFrom);
// 通过二叉树的完整先序序列,创建二叉树的二叉链表存储结构
int idx = 0; // 只是表明createBiTreeV1函数中完整先序的0号单元为根,无其它意义
int ret = createBiTreeV1(completePre, root2, idx);
if (ret == 1)
{
printf("The second binary tree(BT2.dat) created successfully!\n");
int flag = isSame(root, root2);
if (flag == 1)
{
printf("The two binary trees(BT.dat & BT2.dat) are same!\n");
}
else
{
printf("The two binary trees(BT.dat & BT2.dat) are NOT same!\n");
}
}
else
{
printf("The second binary tree(BT2.dat) created failed!\n");
}
if (root2 != NULL)
printf("Destroying binary tree %c ......\n", root2->data);
destroy(root2); // 无论创建成功或失败,最后都将已经开辟的结点空间回收,防止内存泄漏
root2 = NULL; // 销毁之后,该二叉树为空,需要将根结点的指针设置为NULL
break;
}
case 9: // 判断两棵二叉树是否相似
{ // 此对大括号相当于构造一个命名空间,不要去掉
if (root == NULL)
{
printf("The current binary tree(BT.dat) is empty, you may need to create it first!\n");
}
BiTree root3 = NULL;
FILE* fpFrom = fopen("BT3.dat", "r");
if (fpFrom == NULL)
{
printf("File BT3.dat containing the complete PreOrdered sequence cannot be opened, binary tree creation failed!\n");
break;
}
// 读取二叉树的完整先序序列
char completePre[BI_TREE_NODE_MAX * 2];
fscanf(fpFrom, "%s", completePre); // 读取二叉树的完整先序序列
fclose(fpFrom);
// 通过二叉树的完整先序序列,创建二叉树的二叉链表存储结构
int idx = 0; // 只是表明createBiTreeV1函数中完整先序的0号单元为根,无其它意义
int ret = createBiTreeV1(completePre, root3, idx);
if (ret == 1)
{
printf("The third binary tree(BT3.dat) created successfully!\n");
int flag = isSimilar(root, root3);
if (flag == 1)
{
printf("The two binary trees(BT.dat & BT3.dat) are similar!\n");
}
else
{
printf("The two binary trees(BT.dat & BT3.dat) are NOT similar!\n");
}
}
else
{
printf("The third binary tree(BT3.dat) created failed!\n");
}
if (root3 != NULL)
printf("Destroying binary tree %c ......\n", root3->data);
destroy(root3); // 无论创建成功或失败,最后都将已经开辟的结点空间回收,防止内存泄漏
root3 = NULL; // 销毁之后,该二叉树为空,需要将根结点的指针设置为NULL
break;
}
case 10:
if (NULL != root)
{
destroy(root);
root = NULL;
printf("The binary tree destroyed successfully!\n");
}
else
printf("The current binary tree is empty, no need to destroy!\n");
break;
case 11:
system("cls");
break;
case 0:
exit(0);
break;
default:
// If the user enters an alphabetic menu item instead of an integer,
// the buffer needs to be emptied
while ((c = getchar()) != '\n' && c != EOF);
printf("The menu item you entered does not exist, please select it again!\n");
}
}
return 0;
}