一、自定义数据结构
#define _CRT_SECURE_NO_WARNINGS
// Exp4-3_BiTree_FrequentlyExam02
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define BI_TREE_NODE_MAX 64
#define QUEUE_MAX_LENGTH 64
// 二叉树结点的定义
typedef struct _BiNode
{
char data;
struct _BiNode* lchild;
struct _BiNode* rchild;
}BiNode, * BiTree; // BiTree 完全等价于 BiNode *
// 固定长度的循序顺序队列结构体
typedef struct
{
BiTree data[QUEUE_MAX_LENGTH];
int front;
int rear;
} SeqQueue;
二、自定义队列函数
// 判断队列是否为空
int queueIsEmpty(SeqQueue queue)
{
if (queue.front == queue.rear)
return 1;
else
return 0;
}
// 判断队列是否为满
int queueIsFull(SeqQueue queue)
{
if (queue.front == (queue.rear + 1) % QUEUE_MAX_LENGTH)
return 1;
else
return 0;
}
// 当前队列中有多少个元素
int getElemNum(SeqQueue queue)
{
return (queue.rear - queue.front + QUEUE_MAX_LENGTH) % QUEUE_MAX_LENGTH;
}
// 初始化为空队列
void initQueue(SeqQueue& queue)
{
queue.front = queue.rear = QUEUE_MAX_LENGTH - 1;
}
/* 进队一个元素 */
int inQueue(SeqQueue& queue, BiTree e)
{
if (1 == queueIsFull(queue))
return 0; // 队列已满,无法进队
else
{
queue.rear = (queue.rear + 1) % QUEUE_MAX_LENGTH;
queue.data[queue.rear] = e;
return 1; // 进队成功
}
}
/* 出队一个元素 */
int outQueue(SeqQueue& queue, BiTree& e)
{
if (1 == queueIsEmpty(queue))
return 0; // 队列已空,无法出队
else
{
queue.front = (queue.front + 1) % QUEUE_MAX_LENGTH;
e = queue.data[queue.front];
return 1; // 出队成功
}
}
/* 读取队头和队尾元素的信息(不出队列) */
int getFrontRear(SeqQueue queue, BiTree& ef, BiTree& er)
{
if (1 == queueIsEmpty(queue))
return 0; // 队列已空,无队头和队尾
else
{
ef = queue.data[queue.front + 1];
er = queue.data[queue.rear];
return 1;
}
}
三、二叉树有关函数
1.根据二叉树的完整先序序列,递归创建二叉树的二叉链表存储结构
// 根据二叉树的完整先序序列,递归创建二叉树的二叉链表存储结构
int createBiTreeV1(char completePre[], BiTree& root, int& i)
{
// static int i = 0; // 在递归函数中,不要轻易使用static变量
char ch;
BiNode* p;
// 从完整的先序序列中,依次获取各个结点的值(空树为#)
// ch = fgetc(fp);
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;
}
}
2.根据二叉树的先序和中序序列,递归创建二叉树的二叉链表存储结构
// 根据二叉树的先序和中序序列,递归创建二叉树的二叉链表存储结构
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
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;
}
3.递归遍历
// 递归方式先序遍历
void preOrder(BiTree root)
{
if (NULL != root)
{
printf("%2c", root->data);
preOrder(root->lchild);
preOrder(root->rchild);
}
}
// 递归方式中序遍历
void inOrder(BiTree root)
{
if (NULL != root)
{
inOrder(root->lchild);
printf("%2c", root->data);
inOrder(root->rchild);
}
}
// 递归方式后序遍历
void postOrder(BiTree root)
{
if (NULL != root)
{
postOrder(root->lchild);
postOrder(root->rchild);
printf("%2c", root->data);
}
}
4.判断两个结点x和y是否构成兄弟关系
// Function3--判断两个结点x和y是否构成兄弟关系
int isBrothers(BiTree root, char x, char y)
{
if (root == NULL)
return 0;
if (root->lchild != NULL && root->rchild != NULL)
{
if ((root->lchild->data == x && root->rchild->data == y)
|| (root->lchild->data == y && root->rchild->data == x))
return 1;
}
int lRet = isBrothers(root->lchild, x, y);
int rRet = isBrothers(root->rchild, x, y);
if (lRet == 1 || rRet == 1)
return 1;
else
return 0;
}
5.判断结点z是否存在?若为根,则返回1;若存在左子树中,则返回2;若存在右子树中,则返回3;若不存在,则返回0
// Function4--判断结点z是否存在?若为根,则返回1;若存在左子树中,则返回2;若存在右子树中,则返回3;若不存在,则返回0。
int getRegionOfNode(BiTree root, char z)
{
if (root == NULL)
return 0;
if (root->data == z)
return 1;
int lRet = getRegionOfNode(root->lchild, z);
if (lRet != 0)
return 2;
int rRet = getRegionOfNode(root->rchild, z);
if (rRet != 0)
return 3;
return 0;
}
6.获得两个结点x和y的最近公共祖先,若x或y不存在,或者不存在公共祖先则返回NULL
BiNode* getNearestCommonAncestor(BiTree root, char x, char y)
{
if (root == NULL)
return NULL;
int xAncestor = getRegionOfNode(root, x);
int yAncestor = getRegionOfNode(root, y);
if ((xAncestor == 1 && yAncestor != 0) ||
(yAncestor == 1 && xAncestor != 0) ||
(xAncestor == 2 && yAncestor == 3) ||
(yAncestor == 2 && xAncestor == 3))
return root;
if (xAncestor == 2 && yAncestor == 2)
return getNearestCommonAncestor(root->lchild, x, y);
if (xAncestor == 3 && yAncestor == 3)
return getNearestCommonAncestor(root->rchild, x, y);
return NULL;
}
7.获取结点x的所有子孙,依次存入desc数组,变量 i 带回存入子孙的个数
void saveDescendantNode(BiTree root, BiTree desc[], int& i)
{
// static int i = 0;
if (root == NULL)
return;
desc[i++] = root;
saveDescendantNode(root->lchild, desc, i);
saveDescendantNode(root->rchild, desc, i);
}
8.获取结点x的所有子孙,返回子孙结点的个数;若结点x不存在,则返回-1;若结点x为叶子,则返回0
int getAllDescendants(BiTree root, char x, BiTree desc[])
{
int count = 0;
if (root == NULL)
return -1;
if (root->data == x)
{
saveDescendantNode(root->lchild, desc, count);
saveDescendantNode(root->rchild, desc, count);
return count;
}
int lNum = getAllDescendants(root->lchild, x, desc);
if (lNum >= 0)
return lNum;
int rNum = getAllDescendants(root->rchild, x, desc);
if (rNum >= 0)
return rNum;
return -1;
}
9.判断root是否为完全二叉树;若是则返回1,否则返回0
int isCompleteBiTree(BiTree root)
{
SeqQueue Q;
BiNode* p = NULL;
int flag = 0;
if (root == NULL)
return 1; // 空树看成完全二叉树
initQueue(Q);
inQueue(Q, root);
while (!queueIsEmpty(Q))
{
outQueue(Q, p);
//flag = 1 说明前面有结点孩子为空
if (flag == 1 && (p->lchild != NULL || p->rchild != NULL))
return 0;
if (p->lchild != NULL)
inQueue(Q, p->lchild);
else
flag = 1;
if (p->rchild != NULL)
{
if (p->lchild == NULL)
return 0;
inQueue(Q, p->rchild);
}
else
flag = 1;
}
return 1;
}
10.递归方式销毁二叉树
void destroy(BiTree root)
{
if (NULL != root)
{
if (NULL != root->lchild) // 左子树不为空,则递归销毁左子树
destroy(root->lchild);
if (NULL != root->rchild) // 右子树不为空,则递归销毁右子树
destroy(root->rchild);
printf("%c node 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--Check whether the specified two nodes are brothers *\n");
printf("\t* 4--Get the nearest common ancestor of two specified nodes *\n");
printf("\t* 5--Output all descendants of the specified node *\n");
printf("\t* 6--Judge whether a binary tree is a complete binary tree *\n");
printf("\t* 7--Destroy the entire binary tree *\n");
printf("\t* 8--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 it first!\n");
break;
case 3:
if (NULL != root)
{
char x, y;
printf("Please enter values for both nodes(Separate with a space):");
while ((x = getchar()) != '\n' && x != EOF);
scanf("%c %c", &x, &y);
int ret = isBrothers(root, x, y); // 判断两个结点x和y是否构成兄弟关系
if (ret == 1)
{
printf("%c and %c are brothers!\n", x, y);
}
else
{
printf("%c and %c are NOT brothers!\n", x, y);
}
}
else
printf("The current binary tree is empty, please create it first!\n");
break;
case 4:
if (NULL != root)
{
char x, y;
printf("Please enter values for both nodes(Separate with a space):");
while ((x = getchar()) != '\n' && x != EOF);
scanf("%c %c", &x, &y);
BiNode* p = getNearestCommonAncestor(root, x, y); // 求两个结点x和y的最近公共祖先
if (p != NULL)
{
printf("The nearest common ancestor of nodes %c and %c is %c!\n", x, y, p->data);
}
else
{
printf("Nodes %c and %c have no common ancestor!\n", x, y);
}
}
else
printf("The current binary tree is empty, please create it first!\n");
break;
case 5:
if (NULL != root)
{
char x;
printf("Please enter the value of a node:");
while ((x = getchar()) != '\n' && x != EOF);
scanf("%c", &x);
BiTree desc[BI_TREE_NODE_MAX];
// 获取结点x的所有子孙,子孙的数量为返回值num,每个子孙结点的地址存放于desc数组
int num = getAllDescendants(root, x, desc);
if (num > 0)
{
printf("All the descendants of node %c are as follows:\n", x);
for (int i = 0; i < num; i++)
{
printf("%3c", desc[i]->data);
}
printf("\n");
}
else if (num == 0)
{
printf("Node %c is a leaf node!\n", x);
}
else
{
printf("Node %c does not exist!\n", x);
}
}
else
printf("The current binary tree is empty, please create it first!\n");
break;
case 6:
if (NULL != root)
{
// 判断root是否为完全二叉树
int flag = isCompleteBiTree(root);
if (flag == 1)
{
printf("The current binary tree is a complete binary tree!\n");
}
else
{
printf("The current binary tree is NOT a complete binary tree!\n");
}
}
else
printf("The current binary tree is empty, please create it first!\n");
break;
case 7:
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 8:
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;
}