【问题描述】
家谱记载了一个家族的世系繁衍及重要人物事迹。使用树型结构对家谱进行管理,实现查看祖先和子孙个人信息,插入家族成员,删除家族成员的功能。
【基本要求】
(1)采用树型结构完成对家谱成员信息的建立。可利用孩子兄弟表示方法表示树型结构。
(2)完成家谱成员信息查找,插入,修改,删除功能。
(3)判断两个人的家族关系。
(4)进行子孙、祖先、堂兄弟关系的查询。
【扩展要求】
(1)实现树的层次遍历,显示家族每一代的成员。
(2)打印家谱的树型结构操作。
(3)判断两个成员是否属于直系或旁系三代关系
(4)自行设计家谱的其它操作。
程序执行部分截图:
common.h
#pragma once
//常量定义
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define UOVERFLOW -1
#define EINFINITY -2
//类型定义
typedef int Status;
typedef char TElemType;
typedef struct {
char name[20];//姓名
char sex[5];//性别 1为男性 0为女性
long int birthYear;//出生年份
long int endYear;//寿终年份 Infinity表示在世
}Member;
//树的孩子兄弟链表的类型定义:
typedef struct CSTNode {
Member data; //数据域
struct CSTNode* firstChild, * nextSibling;
//左孩子指针,右兄弟指针
}CSTNode, * CSTree, * CSForest; //孩子兄弟链表
typedef struct LSNode {
CSTree data; // 数据域
struct LSNode* next; // 指针域
} LSNode, * LStack; // 结点和链栈类型
typedef struct {
CSTree* elem;
int front; //队头位标
int rear; //队尾位标,队尾元素下一个位置
int maxSize;
}SqQueue;
//获取当前年份
int GetYear() {
time_t nowtime;
time(&nowtime); //获取1970年1月1日0点0分0秒到现在经过的秒数
tm p;
localtime_s(&p, &nowtime);
int year = p.tm_year + 1900;
return year;
}
//打印成员信息
void PrintMemberMessage(Member member) {
printf(" %s",member.name);
printf("\t\t%s", member.sex);
printf("\t %ld", member.birthYear);
if (member.endYear == EINFINITY) {
int age = GetYear() - member.birthYear;
printf("\t\t %d", age);
}
else printf("\t\t %ld", member.endYear);
return;
}
//用空格分割字符串
int partition(char* src, char* par, int pos) {
int i, j;
i = pos;
// 取到第一个非空格字符
while (src[i] == ' ') {
++i;
}
if (src[i] != '\0') {
j = 0;
while ((src[i] != ' ') && (src[i] != '\0')) {
par[j++] = src[i++];
}
par[j] = '\0';
return i;
}
return -1;
}
LStack.h
#pragma once
Status InitStack_LS(LStack& S)
{ // 初始化空链栈S
//Add your code here
S = NULL;
return OK;
}
//进栈
Status Push_LS(LStack& S, CSTree e)
{
LSNode* t;
t = (LSNode*)malloc(sizeof(LSNode)); // 为元素e生成一个新结点
if (NULL == t)
{
return OVERFLOW; // 生成失败,返回
}
t->data = e;
t->next = S;
S = t;
return OK;
}
//出栈
Status Pop_LS(LStack& S, CSTree& e)
{
LSNode* t;
if (NULL == S)
{
return ERROR; // 判断是否为空栈
}
t = S;
e = t->data;
S = t->next;
return OK;
}
//判断栈是否为空
Status StackEmpty_LS(LStack S)
{
if (NULL == S)
{
return TRUE;
}
return FALSE;
}
SqQueue.h
#pragma once
//初始化
Status InitQueue_Sq(SqQueue& Q, int size) {
Q.elem = (CSTree*)(malloc(size * sizeof(CSTree)));
if (NULL == Q.elem) return OVERFLOW;
Q.maxSize = size;
Q.front = Q.rear = 0;
return OK;
}
//出队列
Status DeQueue_Sq(SqQueue& Q, CSTree& e) {
if (Q.front == Q.rear) return ERROR;
e = Q.elem[Q.front];
Q.front = (Q.front + 1) % Q.maxSize;
return OK;
}
//入队列
Status EnQueue_Sq(SqQueue& Q, CSTree& e) {
if ((Q.rear + 1) % Q.maxSize == Q.front) return ERROR;
Q.elem[Q.rear] = e;
Q.rear = (Q.rear + 1) % Q.maxSize;
return OK;
}
//判空
Status QueueEmpty_Sq(SqQueue Q) {
return Q.front == Q.rear ? TRUE : FALSE;
}
CSTreeOperate.h
#pragma once
//以下为定义的树的各项操作
//初始化空树T
Status InitTree(CSTree& T) {
T = NULL;
return OK;
}
//创建根结点e和n棵子树的树
CSTree MakeTree(Member e, int n, ...) {
int i; CSTree t, p, pi;
va_list argptr;
t = (CSTNode*)malloc(sizeof(CSTNode));
if (NULL == t) return NULL;
t->data = e;
t->firstChild = t->nextSibling = NULL;
if (n <= 0) return t;
va_start(argptr, n);
p = va_arg(argptr, CSTree);
t->firstChild = p;
pi = p;
for (i = 1; i < n; i++) {
p = va_arg(argptr, CSTree);
pi->nextSibling = p;
pi = p;
}
va_end(argptr);
return t;
}
//创建一棵树(实质为创建二叉树)
CSTree CreateTree(Member* members, int& i)
{ // 按str先序构造二叉树的二叉链表,i为str的当前位标,初值为0
CSTree T;
InitTree(T);
Member member = members[i++];
if (!strcmp(member.name,"#"))
{
InitTree(T); // 空树
}
else
{
T = MakeTree(member, 0); // 构造结点ch
T->firstChild = CreateTree(members, i); // 构造孩子
T->nextSibling = CreateTree(members, i); // 构造兄弟
}
return T;
}
//求结点在树中的层次
int NodeLevel(CSTree T, char* target) {
if (T == NULL) {
return -1; // 树为空,返回-1表示未找到
}
if (T->data.name == target) {
return 0; // 根节点就是目标节点,层次为0
}
int level = -1; // 初始化层次为-1,表示未找到
CSTree p = T->firstChild;
while (p != NULL) {
int childLevel = NodeLevel(p, target); // 递归求子节点的层次
if (childLevel != -1) {
level = childLevel + 1; // 子节点层次加1即为当前节点的层次
break;
}
p = p->nextSibling;
}
return level;
}
//层序遍历一棵树(队列)
void LevelOrder(CSTree T)
{
SqQueue Q;
InitQueue_Sq(Q, 20);
CSTree p = T;
EnQueue_Sq(Q, p);
while (!QueueEmpty_Sq(Q))
{
DeQueue_Sq(Q, p);
PrintMemberMessage(p->data);
printf("\t\t【%d世】", NodeLevel(T, p->data.name) + 1);
printf("\n");
p = p->firstChild;
while (p)
{
EnQueue_Sq(Q, p);
p = p->nextSibling;
}
}
}
//森林的查找
CSTNode* Search(CSForest F, char name[20]) {
//查找森林F中的结点e的name并返回其指针
CSTNode* result = NULL;
if (NULL == F) return NULL;
if (!strcmp(F->data.name, name)) return F;
if ((result = Search(F->firstChild, name)) != NULL) return result;
return Search(F->nextSibling, name);
}
//插入一个结点
Status InsertNode(CSTree T, CSTree FaNode, Member ChNode) {
if (NULL == T) return ERROR;
if (NULL == T) return ERROR;
else {
CSTNode* t = (CSTNode*)malloc(sizeof(CSTNode));
if (NULL == t) return UOVERFLOW;
t->data = ChNode;
if (!FaNode->firstChild) {
FaNode->firstChild = t;
}
else {
CSTree q = FaNode->firstChild;
while (q->nextSibling)
{
q = q->nextSibling;
}
q->nextSibling = t;
}
t->firstChild = t->nextSibling = NULL;
}
return OK;
}
//删除结点(最近一级父节点)
//销毁树
void DestroyTree(CSTree& T) {
if (T) {
if (T->firstChild)
DestroyTree(T->firstChild);
else if (T->nextSibling)
DestroyTree(T->nextSibling);
free(T);
}
}
//重新连接断口
void Reconnect(CSTNode* rootNode, CSTNode* desNode) {
if (rootNode->firstChild == desNode) {
rootNode->firstChild = desNode->nextSibling;
}
if (rootNode->nextSibling == desNode) {
rootNode->nextSibling = desNode->nextSibling;
}
desNode->nextSibling = NULL;
DestroyTree(desNode);
}
Status DeleteNode(CSTree T, char rootNode[20], char desNode[20]) {
if (NULL == T) return OK;
CSTree p = Search(T, rootNode);
CSTree q = Search(T, desNode);
if (NULL == p || NULL == q) return ERROR;
if (!p->firstChild) return ERROR;
if (p->firstChild == q) {
if (!p || !q) return ERROR;
Reconnect(p, q);
}
else {
p = p->firstChild;
while (p && p->nextSibling != q)
{
p = p->nextSibling;
}
if (!p || !q) return ERROR;
Reconnect(p, q);
}
return OK;
}
//判断成员关系
//是否是直系(后代结点是否在前代结点的子树上)
Status IsDirectRelationship(CSTree T, CSTree p, CSTree q) {
if (NULL == T) return UOVERFLOW;
CSTree t = p;
while (t->nextSibling)
{
if (t->nextSibling == q) return 1;
t = t->nextSibling;
}
p = p->firstChild;
LStack S; InitStack_LS(S);
while (p || !StackEmpty_LS(S)) {
while (p) {
if (p == q) return 1;
Push_LS(S, p);
p = p->firstChild;
}
Pop_LS(S, p);
if (p) p = p->nextSibling;
}
return 0;
}
//是否为旁系(后代结点是否在前代结点兄弟树上)
Status IsCollateralRelationship(CSTree T, CSTree p, CSTree q) {
if (!IsDirectRelationship(T, p, q)) return 1;
return 0;
}
GenealogyOperate.h
#pragma once
//函数参数
typedef void(*handler)(CSTree);
typedef void(*callback)(Member&,CSTree T);
typedef void(*_callback)(Member&, Member&, CSTree T);
//家谱管理系统操作
Member* GetMembers(); /*获取成员数组*/
Member EmptyNode(); /*空结点(是否创建空树)*/
Member* CreateMemeberTree(); /*创建家谱树结构数组*/
void CreateFamilyTree(CSTree& T); /*创建家谱*/
Status IsContinue(); /*是否继续*/
void Prompt(CSTree T,const char ch1[20], const char ch2[20], handler h); /*提示框*/
void ShowFamilyMembers(CSTree T); /*显示家谱成员(层序遍历)*/
void SearchFamilyMember(CSTree T); /*查找家谱成员*/
void CascadingMenu(CSTree T); /*修改家谱成员的级联菜单*/
void ChangeMemberName(CSTree T,CSTree member); /*修改成员姓名*/
void ChangeMemberSex(CSTree T, CSTree member); /*修改成员性别*/
void ChangeMemberBirthYear(CSTree T, CSTree member); /*修改成员出生日期*/
void ChangeMemberEndYear(CSTree T, CSTree member); /*修改成员寿终日期*/
void AddMember(CSTree T); /*添加家谱成员*/
void StepPrompt(CSTree T,Member& m, callback c1, callback c2); /*步骤提示框*/
void AddName(Member& chNode,CSTree T); /*输入添加成员姓名*/
void AddSex(Member& chNode, CSTree T); /*输入添加成员性别*/
void AddBirthYear(Member& chNode, CSTree T); /*输入添加成员出生年*/
void AddEndYear(Member& chNode, CSTree T); /*输入添加成员寿终年*/
void DeleteMember(CSTree T); /*删除家谱成员*/
void StepTip(Member& ch, Member& fa, CSTree T,callback c1,_callback c2); /*步骤提示*/
void CheckFaNode(Member& fa,Member& ch,CSTree T); /*输入检测父节点*/
void CheckChNode(Member& m,CSTree T); /*输入检测子节点*/
void CoutMembers(CSTree T); /*统计家谱成员*/
int CoutMembersNum(CSTree T,int& num); /*统计家谱成员数量*/
float CoutLife(CSTree T, float& total); /*统计家谱成员总寿命*/
int CoutDeadMember(CSTree T, int& num); /*统计寿终成员数量*/
float CountMaleNum(CSTree T,float& maleNum); /*统计男性数量*/
float CountFemaleNum(CSTree T,float& femaleNum); /*统计女性数量*/
void Members_Relationship(CSTree T); /*家谱成员关系*/
void CheckPreNode(Member& pre, Member& des, CSTree T); /*输入检测前代节点*/
void CheckDesNode(Member& des, CSTree T); /*输入检测后代结点*/
int GetRelationshipBySex(CSTree p, CSTree q); /*通过性别获取关系*/
void InquireRelationship(CSTree T);
void InquireSons(CSTree T,CSTree t);
void InquireAncestor(CSTree T, CSTree t);
void InquireCousin(CSTree T, CSTree t);
void PrintAsTree(CSTree b); /*打印家谱树*/
void Menu(CSTree T); /*菜单*/
//以下为创建家谱树过程
//获取成员数组
Member* GetMembers() {
//创建家谱成员数组
Member* members = (Member*)malloc(sizeof(Member) * 50);
if (members == NULL) return NULL;
//打开文件
FILE* fp = NULL;
fopen_s(&fp, "data.txt", "r");
if (!fp) {
printf("失败");
return NULL;
}
//开始添加成员到数组
int j = 0;
while (!feof(fp))
{
//行式读取文件
char src[1024] = { '\0' };
fgets(src, 1024, fp);
int i = 0;
char par[20];
int pos = 0;
//用空格分割字符串
while ((pos = partition(src, par, pos)) != -1) {
if (i == 0) strcpy_s(members[j].name, par);
else if (i == 1) strcpy_s(members[j].sex, par);
else if (i == 2) members[j].birthYear = atoi(par);
else if (i == 3) members[j].endYear = atoi(par);
i++;
}
j++;
}
//关闭文件
fclose(fp);
return members;
}
//空结点(是否创建空树)
Member EmptyNode() {
return { "#","#",0,0 };
}
//创建家谱树结构数组
Member* CreateMemeberTree() {
//获取家谱成员
Member* members = GetMembers();
//创建家谱树结构数组
Member* memberTree = (Member*)malloc(sizeof(Member) * 80);
if (NULL == memberTree) return NULL;
memberTree[0] = members[0]; memberTree[1] = members[1]; memberTree[2] = members[2]; memberTree[3] = members[3]; memberTree[4] = EmptyNode();
memberTree[5] = members[4]; memberTree[6] = members[5]; memberTree[7] = members[6]; memberTree[8] = EmptyNode(); memberTree[9] = EmptyNode();
memberTree[10] = EmptyNode(); memberTree[11] = EmptyNode(); memberTree[12] = members[7]; memberTree[13] = members[8]; memberTree[14] = members[9];
memberTree[15] = EmptyNode(); memberTree[16] = EmptyNode(); memberTree[17] = EmptyNode(); memberTree[18] = members[10]; memberTree[19] = EmptyNode();
memberTree[20] = members[11]; memberTree[21] = members[12]; memberTree[22] = members[13]; memberTree[23] = EmptyNode(); memberTree[24] = EmptyNode();
memberTree[25] = EmptyNode(); memberTree[26] = EmptyNode(); memberTree[27] = members[14]; memberTree[28] = EmptyNode(); memberTree[29] = members[15];
memberTree[30] = members[16]; memberTree[31] = EmptyNode(); memberTree[32] = members[17]; memberTree[33] = members[18]; memberTree[34] = members[19];
memberTree[35] = members[20]; memberTree[36] = EmptyNode(); memberTree[37] = EmptyNode(); memberTree[38] = members[21]; memberTree[39] = EmptyNode();
memberTree[40] =EmptyNode(); memberTree[41] = members[22]; memberTree[42] =members[23]; memberTree[43] = EmptyNode(); memberTree[44] = EmptyNode();
memberTree[45] = EmptyNode(); memberTree[46] =members[24]; memberTree[47] = members[25]; memberTree[48] = EmptyNode(); memberTree[49] = members[26];
memberTree[50] = members[27]; memberTree[51] = EmptyNode(); memberTree[52] = EmptyNode(); memberTree[53] = members[28]; memberTree[54] = members[29];
memberTree[55] = EmptyNode(); memberTree[56] = EmptyNode(); memberTree[57] = members[30]; memberTree[58] = EmptyNode(); memberTree[59] = members[31];
memberTree[60] = EmptyNode(); memberTree[61] = members[32]; memberTree[62] = EmptyNode(); memberTree[63] = EmptyNode(); memberTree[64]=members[33];
memberTree[65] = members[34]; memberTree[66] = members[35]; memberTree[67] = EmptyNode(); memberTree[68] = EmptyNode(); memberTree[69] = members[36];
memberTree[70] = members[37]; memberTree[71] = EmptyNode(); memberTree[72] = EmptyNode(); memberTree[73] = EmptyNode(); memberTree[74] = EmptyNode();
memberTree[75] = EmptyNode(); memberTree[76] = EmptyNode();
return memberTree;
}
//创建家谱
void CreateFamilyTree(CSTree& T) {
//初始化家谱树
InitTree(T);
//创建家谱树
int i = 0;
Member* membersTree = CreateMemeberTree();
T=CreateTree(membersTree, i);
}
//是否继续
Status IsContinue() {
char ch = '\0';
rewind(stdin);
scanf_s("%c", &ch, 1);
if (ch == '1') return 1; //继续
else if (ch == '0') return 0; //退出
else return -1; //ERROR;
}
//提示框
void Prompt(CSTree T, const char ch1[20], const char ch2[20], handler h)
{
printf("提示(1:%s,0:%s):", ch1, ch2);
int choice = 0;
char ch = '\0';
rewind(stdin);
scanf_s("%c", &ch, 1);
if (ch == '1') choice = 1; //继续
else if (ch == '0') choice = 0; //退出
else choice = -1; //ERROR;
if (choice == 1) h(T);
else if (choice == 0) return;
else if (choice == -1) {
printf("输入有误,");
Prompt(T, ch1, ch2, h);
}
return;
}
//显示家谱成员(层序遍历)
void ShowFamilyMembers(CSTree T) {
system("cls");
printf("╔-------------------------------------------------------------╗\n");
printf("\t\t\t$ 显示家谱成员 $\n\n");
printf(" 姓名\t\t性别\t出生日期\t寿终日期(年龄)\n");
LevelOrder(T);
printf("\n");
printf("╚-------------------------------------------------------------╝\n");
Prompt(T, "返回", "退出", Menu);
}
//查找家谱成员
void SearchFamilyMember(CSTree T) {
printf("请输入您要查找的成员的姓名:");
char name[20] = { 0 };
rewind(stdin);
scanf_s("%s", &name, 20);
CSTree member = Search(T, name);
if (member!=NULL) {
if (!strcmp(name, member->data.name)) {
system("cls");
printf("╔-------------------------------------------------------------╗\n");
printf("\t\t\t$ 查找家谱成员 $\n\n");
printf(" 姓名\t\t性别\t出生日期\t寿终日期(年龄)\n");
PrintMemberMessage(member->data);
printf("\t\t【%d世】", NodeLevel(T, member->data.name) + 1);
printf("\n\n");
printf("╚-------------------------------------------------------------╝\n");
Prompt(T, "返回", "退出", Menu);
}
else {
printf("家谱中不存在此人,请检查拼写错误\n");
Prompt(T, "重新输入", "退出", SearchFamilyMember);
}
}
else {
printf("家谱中不存在此人,请检查拼写错误\n");
Prompt(T, "重新输入", "退出", SearchFamilyMember);
}
return;
}
//修改家谱成员的级联菜单
void CascadingMenu(CSTree T) {
printf("请输入您要修改的成员的姓名:");
char name[20] = { 0 };
rewind(stdin);
scanf_s("%s", &name, 20);
CSTree member = Search(T, name);
if (!member) {
printf("家谱中不存在此人,请检查拼写错误\n");
Prompt(T, "重新输入", "退出", CascadingMenu);
return;
}
system("cls");
printf("查找成功!!!\n");
up:
printf("\t\t\t\t\t\t\t$ 修改家谱成员 $\n\n");
printf("\t\t\t\t\t\t\t1 # 修改成员姓名\n");
printf("\t\t\t\t\t\t\t2 # 修改成员性别\n");
printf("\t\t\t\t\t\t\t3 # 修改出生日期\n");
printf("\t\t\t\t\t\t\t4 # 修改寿终日期\n");
printf("\t\t\t\t\t\t\t5 # 返回主菜单\n");
printf("\t\t\t\t\t\t\t0 # 退出\n\n");
printf("\t\t\t\t\t\t╚-----------------------------╝\n");
//菜单功能实现
printf("请输入您要选择的功能:");
char choice = '0';
rewind(stdin);
scanf_s("%c", &choice, 1);
switch (choice)
{
case '1': ChangeMemberName(T,member);
break;
case '2':ChangeMemberSex(T, member);
break;
case '3':ChangeMemberBirthYear(T, member);
break;
case '4':ChangeMemberEndYear(T, member);
break;
case '5':Menu(T);
break;
case '0':return;
default: {printf("请输入正确的数字!!!\n"); goto up; };
break;
}
return;
}
//修改成员姓名
void ChangeMemberName(CSTree T,CSTree member) {
printf("请输入修改后的姓名:");
char name[20] = { 0 };
rewind(stdin);
scanf_s("%s", &name,20);
strcpy_s(member->data.name,name);
printf("修改成功,");
Prompt(T, "返回主菜单", "退出",Menu);
return;
}
//修改成员性别
void ChangeMemberSex(CSTree T, CSTree member) {
printf("请输入修改后的性别:");
char sex[10] = { 0 };
rewind(stdin);
scanf_s("%s", &sex, 10);
if (strcmp(sex, "男")&&strcmp(sex, "女")) {
printf("请输入正确的性别!!!\n");
ChangeMemberSex(T, member);
}
strcpy_s(member->data.sex, sex);
printf("修改成功,");
Prompt(T, "返回主菜单", "退出", Menu);
return;
}
//修改成员出生日期
void ChangeMemberBirthYear(CSTree T, CSTree member) {
printf("请输入修改后的出生年:");
char birthYear[10] = { 0 };
rewind(stdin);
scanf_s("%s", &birthYear, 10);
member->data.birthYear = atoi(birthYear);
printf("修改成功,");
Prompt(T, "返回主菜单", "退出", Menu);
return;
}
//修改成员寿终日期
void ChangeMemberEndYear(CSTree T, CSTree member) {
printf("请输入修改后的寿终年(如果成员在世,请输入-2):");
char endYear[10] = { 0 };
rewind(stdin);
scanf_s("%s", &endYear, 10);
member->data.endYear = atoi(endYear);
printf("修改成功,");
Prompt(T, "返回主菜单", "退出", Menu);
return;
}
//添加家谱成员
//步骤提示框
void StepPrompt(CSTree T,Member& m,callback c1,callback c2) {
printf("提示(1:下一步,0:上一步):");
int choice = 0;
char ch = '\0';
rewind(stdin);
scanf_s("%c", &ch, 1);
if (ch == '1') choice = 1; //下一步
else if (ch == '0') choice = 0; //上一步
else choice = -1; //ERROR;
if (choice == 1) c1(m,T);
else if (choice == 0) c2(m,T);
else if (choice == -1) {
printf("输入有误,");
StepPrompt(T,m, c1, c2);
}
return;
}
void AddMember(CSTree T) {
printf("请输入被添加成员的父(母)亲的姓名:");
char faName[20] = { 0 };
rewind(stdin);
scanf_s("%s", &faName,20);
CSTree faNode = Search(T, faName);
if (NULL == faNode) {
printf("家谱中不存在此人!!!\n");
AddMember(T);
return;
}
Member chNode = EmptyNode();
AddName(chNode,T);
InsertNode(T, faNode, chNode);
printf("添加成功\n");
Prompt(T, "返回", "退出", Menu);
return;
}
//输入添加成员姓名
void AddName(Member& chNode, CSTree T) {
printf("请输入添加成员的姓名:");
char chName[20] = { 0 };
rewind(stdin);
scanf_s("%s", &chName, 20);
strcpy_s(chNode.name, chName);
StepPrompt(T,chNode, AddSex, AddName);
return;
}
//输入添加成员性别
void AddSex(Member& chNode, CSTree T) {
printf("请输入添加成员的性别:");
char chSex[10] = { 0 };
rewind(stdin);
scanf_s("%s", &chSex, 10);
strcpy_s(chNode.sex, chSex);
StepPrompt(T,chNode, AddBirthYear, AddSex);
return;
}
//输入添加成员出生年
void AddBirthYear(Member& chNode, CSTree T) {
printf("请输入添加成员的出生年:");
char chBirthYear[10] = { 0 };
rewind(stdin);
scanf_s("%s", &chBirthYear, 10);
if (!atoi(chBirthYear)) {
printf("请输入正确的年份\n");
AddBirthYear(chNode,T);
}
chNode.birthYear = atoi(chBirthYear);
StepPrompt(T,chNode,AddEndYear,AddBirthYear);
return;
}
//输入添加成员寿终年
void AddEndYear(Member& chNode,CSTree T) {
printf("请输入添加成员的寿终年(若成员在世请输入-2):");
char chEndYear[10] = { 0 };
rewind(stdin);
scanf_s("%s", &chEndYear, 10);
if (!atoi(chEndYear)) {
printf("请输入正确的年份\n");
AddEndYear(chNode,T);
}
chNode.endYear = atoi(chEndYear);
return;
}
//删除家谱成员
//步骤提示
void StepTip(Member& ch,Member& fa,CSTree T,callback c1,_callback c2) {
printf("提示(1:下一步,0:上一步):");
int choice = IsContinue();
if (choice == 1) c1(ch, T);
else if (choice == 0) c2(ch,fa,T);
else StepTip(ch, fa, T,c1,c2);
return;
}
void DeleteMember(CSTree T) {
Member fa = EmptyNode(); Member ch = EmptyNode();
CheckFaNode(fa,ch,T);
if (DeleteNode(T, fa.name, ch.name)) {
printf("删除成功!!!\n");
}
else {
printf("以上输入的两名成员不存在父(母)子(女)关系\n");
}
Prompt(T, "返回", "退出", Menu);
return;
}
//输入检测父节点
void CheckFaNode(Member& fa,Member& ch,CSTree T) {
printf("请输入删除成员的父(母)亲的姓名:");
char faName[20] = { 0 };
rewind(stdin);
scanf_s("%s", &faName, 20);
CSTree faNode = Search(T,faName);
if (!faNode) {
printf("家谱中不存在此人!!!\n");
CheckFaNode(fa,ch,T);
return;
}
else {
printf("查找成功!!!\n");
strcpy_s(fa.name, faName);
}
StepTip(ch, fa, T,CheckChNode, CheckFaNode);
return;
}
//输入检测子节点
void CheckChNode(Member& m, CSTree T) {
printf("请输入删除成员的姓名:");
char chName[20] = { 0 };
rewind(stdin);
scanf_s("%s", &chName, 20);
CSTree chNode = Search(T, chName);
if (!chNode) {
printf("家谱中不存在此人!!!");
CheckChNode(m,T);
return;
}
printf("查找成功!!!\n");
strcpy_s(m.name, chName);
return ;
}
//统计家谱成员
void CoutMembers(CSTree T) {
int num = 0;CoutMembersNum(T, num); //家谱成员总数
float totalLife = 0;CoutLife(T, totalLife); //寿终成员总寿命
int deadNum = 0; CoutDeadMember(T, deadNum);
float maleNum = 0; CountMaleNum(T, maleNum); //男性成员数
float femaleNum = 0; CountFemaleNum(T, femaleNum);//女性成员总数
system("cls");
printf("\t\t\t\t\t\t╔-----------------------------╗\n");
printf("\t\t\t\t\t\t\t$ 统计家谱成员 $\n");
printf("\t\t\t\t\t\t\t成员数量:%d\n", num);
printf("\t\t\t\t\t\t\t平均寿命:%.2f岁\n",totalLife / deadNum);
printf("\t\t\t\t\t\t\t男性比例:%.2f%%\n", 100 * maleNum / num);
printf("\t\t\t\t\t\t\t女性比例:%.2f%%\n", 100 * femaleNum / num);
printf("\t\t\t\t\t\t╚-----------------------------╝\n");
Prompt(T, "返回", "退出", Menu);
}
//统计成员数量
int CoutMembersNum(CSTree T,int& num) {
if (NULL == T) return num;
num++;
CoutMembersNum(T->firstChild,num);
CoutMembersNum(T->nextSibling,num);
return num;
}
//统计成员总寿命
float CoutLife(CSTree T,float &total) {
if (NULL == T) return total;
if (T->data.endYear != EINFINITY) {
total += T->data.endYear - T->data.birthYear;
}
CoutLife(T->firstChild,total);
CoutLife(T->nextSibling,total);
return total;
}
//统计寿终成员数量
int CoutDeadMember(CSTree T, int& num) {
if (NULL == T) return num;
if (T->data.endYear != EINFINITY) num++;
CoutDeadMember(T->firstChild, num);
CoutDeadMember(T->nextSibling, num);
return num;
}
//统计男性数量
float CountMaleNum(CSTree T,float &maleNum) {
if (NULL == T) return maleNum;
if (!strcmp(T->data.sex,"男")) {
maleNum++;
}
CountMaleNum(T->firstChild,maleNum);
CountMaleNum(T->nextSibling,maleNum);
return maleNum;
}
//统计女性数量
float CountFemaleNum(CSTree T,float &femaleNum) {
if (NULL == T) return femaleNum;
if (!strcmp(T->data.sex, "女")) {
femaleNum++;
}
CountFemaleNum(T->firstChild,femaleNum);
CountFemaleNum(T->nextSibling,femaleNum);
return femaleNum;
}
//判断家谱中两人的关系
void Members_Relationship(CSTree T) {
Member pre = EmptyNode(); Member des = EmptyNode();
CheckPreNode(pre, des, T);
CSTree p = Search(T, pre.name);
CSTree q = Search(T, des.name);
int Level1 = NodeLevel(T, p->data.name);
int Level2 = NodeLevel(T, q->data.name);
int result = abs(Level1 - Level2); int res = GetRelationshipBySex(p, q);
system("cls");
//成员1为前代,成员2为后代(直系)
printf("╔-----------------------------╗\n");
printf("\t$ 家谱成员关系 $\n");
if (IsDirectRelationship(T, p, q)) {
if (result == 0) {
if (res == 0) printf("\t他们是兄弟关系\n");
else if (res == 1) printf("\t他们是兄妹关系\n");
else if (res == 2) printf("\t他们是姐弟关系\n");
else printf("\t她们是姐妹关系\n");
}
else if (result == 1) {
if (res == 0) printf("\t他们是父子关系\n");
else if (res == 1) printf("\t他们是父女关系\n");
else if (res == 2) printf("\t他们是母子关系\n");
else printf("\t她们是母女关系\n");
}
else if (result == 2) {
if (res == 0) printf("\t他们是爷孙关系\n");
else if (res == 1) printf("\t他们是爷孙关系\n");
else if (res == 2) printf("\t他们是婆孙关系\n");
else printf("\t她们是婆孙关系\n");
}
else if (result >= 3) printf("\t他们是祖孙关系\n");
}
//成员1为后代,成员2为前代(直系)
else if (IsDirectRelationship(T, q, p)) {
if (result == 0) {
if (res == 0) printf("\t他们是兄弟关系\n");
else if (res == 1) printf("\t他们是姐弟关系\n");
else if (res == 2) printf("\t他们是兄妹关系\n");
else printf("\t她们是姐妹关系\n");
}
else if (result == 1) {
if (res == 0) printf("\t他们是父子关系\n");
else if (res == 1) printf("\t他们是母子关系\n");
else if (res == 2) printf("\t他们是父女关系\n");
else printf("\t她们是母女关系\n");
}
else if (result == 2) {
if (res == 0) printf("\t他们是爷孙关系\n");
else if (res == 1) printf("\t他们是婆孙关系\n");
else if (res == 2) printf("\t他们是爷孙关系\n");
else printf("\t她们是婆孙关系\n");
}
else if (result >= 3) printf("\t他们是祖孙关系\n");
}
//成员1为前代,成员2为后代(旁系)
else if (IsCollateralRelationship(T, p, q)) {
if (result == 0) {
if (res == 0) printf("\t他们是堂兄弟关系\n");
else if (res == 1) printf("\t他们堂兄妹关系\n");
else if (res == 2) printf("\t他们堂姐弟关系\n");
else printf("她们是堂姐妹关系\n");
}
else if (result == 1) {
if (res == 0) printf("\t他们是叔侄关系\n");
else if (res == 1) printf("\t他们是叔侄关系\n");
else if (res == 2) printf("\t他们是婶侄关系\n");
else printf("\t她们是婶侄关系\n");
}
else if (result == 2) printf("\t他们是三代内旁系\n");
else if (result >= 3) printf("\t他们是三代外旁系\n");
}
//成员1为后代, 成员2为前代(旁系)
else if (IsCollateralRelationship(T, q, p)) {
if (result == 0) {
if (res == 0) printf("\t他们是堂兄弟关系\n");
else if (res == 1) printf("\t他们堂姐弟关系\n");
else if (res == 2) printf("\t他们堂兄妹关系\n");
else printf("\t她们是堂姐妹关系\n");
}
else if (result == 1) {
if (res == 0) printf("\t他们是叔侄关系\n");
else if (res == 1) printf("\t他们是婶侄关系\n");
else if (res == 2) printf("\t他们是叔侄关系\n");
else printf("\t她们是婶侄关系\n");
}
else if (result == 2) printf("\t他们是三代内旁系\n");
else if (result >= 3) printf("\t他们是三代外旁系\n");
}
printf("╚-----------------------------╝\n");
Prompt(T, "返回", "退出", Menu);
return;
}
//输入查找成员1
void CheckPreNode(Member& pre, Member& des, CSTree T) {
printf("请输入第一个成员的姓名:");
char preName[20] = { 0 };
rewind(stdin);
scanf_s("%s", &preName, 20);
CSTree preNode = Search(T, preName);
if (!preNode) {
printf("家谱中不存在此人!!!\n");
CheckPreNode(pre, des, T);
return;
}
else {
printf("查找成功!!!\n");
strcpy_s(pre.name, preName);
}
StepTip(des, pre, T, CheckDesNode, CheckPreNode);
return;
}
//输入查找成员2
void CheckDesNode(Member& des, CSTree T) {
printf("请输入第二个成员的姓名:");
char desName[20] = { 0 };
rewind(stdin);
scanf_s("%s", &desName, 20);
CSTree desNode = Search(T, desName);
if (!desNode) {
printf("家谱中不存在此人!!!");
CheckDesNode(des, T);
return;
}
printf("查找成功!!!\n");
strcpy_s(des.name, desName);
return;
}
//由性别输出具体关系
int GetRelationshipBySex(CSTree p,CSTree q) {
if (strcmp(p->data.sex, "男") == 0 && strcmp(q->data.sex, "男") == 0) return 0; //两个成员均为男
else if (strcmp(p->data.sex, "男") == 0 && strcmp(q->data.sex, "女") == 0) return 1;//前男后女
else if (strcmp(p->data.sex, "女") == 0 && strcmp(q->data.sex, "男") == 0) return 2;//前女后男
else if (strcmp(p->data.sex, "女") == 0 && strcmp(q->data.sex, "女") == 0) return 3;//均为女
}
//关系的查询
void InquireRelationship(CSTree T) {
printf("请输入您要查询的成员的姓名:");
char name[20] = { 0 };
rewind(stdin);
scanf_s("%s", &name, 20);
CSTree member = Search(T, name);
if (!member) {
printf("家谱中不存在此人,请检查拼写错误\n");
Prompt(T, "重新输入", "退出",InquireRelationship);
return;
}
system("cls");
printf("查找成功!!!\n");
up:
printf("\t\t\t\t\t\t\t$ 查询成员关系 $\n\n");
printf("\t\t\t\t\t\t\t1 # 查询子孙\n");
printf("\t\t\t\t\t\t\t2 # 查询祖先\n");
printf("\t\t\t\t\t\t\t3 # 查询堂兄弟\n");
printf("\t\t\t\t\t\t\t4 # 返回主菜单\n");
printf("\t\t\t\t\t\t\t0 # 退出\n\n");
printf("\t\t\t\t\t\t╚-----------------------------╝\n");
//菜单功能实现
printf("请输入您要选择的功能:");
char choice = '0';
rewind(stdin);
scanf_s("%c", &choice, 1);
switch (choice)
{
case '1': InquireSons(T, member);
break;
case '2':InquireAncestor(T, member);
break;
case '3':InquireCousin(T, member);
break;
case '4':Menu(T);
break;
case '0':return;
default: {printf("请输入正确的数字!!!\n"); goto up; };
break;
}
return;
}
//查询子孙
void InquireSons(CSTree T,CSTree t) {
if (!t->firstChild) {
printf(" %s不存在子孙\n", t->data.name);
Prompt(T, "返回", "退出", Menu);
return;
}
system("cls");
printf(" %s的子孙如下:\n\n", t->data.name);
SqQueue Q;
InitQueue_Sq(Q, 20);
CSTree p = t;
EnQueue_Sq(Q, p);
printf(" 姓名\t\t性别\t出生日期\t寿终日期(年龄)\n");
while (!QueueEmpty_Sq(Q))
{
DeQueue_Sq(Q, p);
if (p != t) {
PrintMemberMessage(p->data);
printf("\t\t【%d代】", NodeLevel(t, p->data.name));
printf("\n");
}
p = p->firstChild;
while (p)
{
EnQueue_Sq(Q, p);
p = p->nextSibling;
}
}
printf("\n");
Prompt(T, "返回", "退出",Menu);
return;
}
//查询祖先
void InquireAncestor(CSTree T, CSTree t) {
LStack S;
InitStack_LS(S);
while (T || !StackEmpty_LS(S))
{
while (T)
{
Push_LS(S, T);
T = T->firstChild;
}
Pop_LS(S, T);//STL要求这样做的
if (T == t) break;
if (T) T = T->nextSibling;//root可以为空的
}
CSTree e;
if (StackEmpty_LS(S)) {
printf("家谱中没有%s的祖先\n", t->data.name);
Prompt(T, "返回", "退出", Menu);
return;
}
system("cls");
printf(" %s的祖先如下:\n", t->data.name);
printf(" 姓名\t\t性别\t出生日期\t寿终日期(年龄)\n");
while (!StackEmpty_LS(S))
{
Pop_LS(S, e);
PrintMemberMessage(e->data);
printf("\n");
}
printf("\n");
Prompt(T, "返回", "退出", Menu);
return;
}
//查询堂兄弟
void InquireCousin(CSTree T, CSTree t){
int i = NodeLevel(T, t->data.name);
SqQueue Q;
InitQueue_Sq(Q, 20);
CSTree p = T;
EnQueue_Sq(Q, p);
while (!QueueEmpty_Sq(Q))
{
if (NodeLevel(T, Q.elem[Q.front]->data.name) == i) break;
DeQueue_Sq(Q, p);
p = p->firstChild;
while (p)
{
EnQueue_Sq(Q, p);
p = p->nextSibling;
}
}
if (QueueEmpty_Sq(Q)) {
printf("%s不存在堂兄弟\n", t->data.name);
Prompt(T, "返回", "退出", Menu);
return;
}
CSTree e;
system("cls");
printf(" %s的堂兄弟如下:\n", t->data.name);
printf(" 姓名\t\t性别\t出生日期\t寿终日期(年龄)\n");
while (!QueueEmpty_Sq(Q)) {
DeQueue_Sq(Q, e);
if (IsCollateralRelationship(T, t, e)&&IsCollateralRelationship(T, e, t)) {
if (t != e) {
PrintMemberMessage(e->data);
printf("\n");
}
}
}
Prompt(T, "返回", "退出", Menu);
return;
}
//将树打印为家谱树结构
void PrintAsTree(CSTree b)
{
CSTree stack[20], p; //stack[top]为指针数组
int level[20], top, i, n;
//top为stack栈指针、levle[20]为结点层次数组
system("cls");
if (b != NULL)
{
top = 1;
stack[top] = b->firstChild; //根节点入栈
level[top] = 0;
printf("\t\t\t\t\t ╔------------------------------------╗\n");
printf("\t\t\t\t\t\t\t $ 家谱树 $\n\n");
printf("\t\t\t\t\t\t%s\n", b->data.name);
while (top > 0) //栈不为空时,循环继续
{
p = stack[top]; //退栈并凹入显示该节点值
n = level[top];
//如果要打印空格则先打印|
for (i = 1; i < n; i++) {
//n为输出的空格数
if (i == 1) printf("\t\t\t\t\t\t");
printf("|");
printf(" ");
}
if (NodeLevel(b, p->data.name) == 1) printf("\t\t\t\t\t\t");
printf("|—>%s\n", p->data.name);
top--; //根节点出栈
if (p->nextSibling != NULL)
{
top++;
stack[top] = p->nextSibling; //将兄弟节点入栈
level[top] = NodeLevel(b, p->nextSibling->data.name);//计算结点层次
}
if (p->firstChild != NULL)
{
top++;
stack[top] = p->firstChild; //将孩子节点入栈
level[top] = NodeLevel(b, p->firstChild->data.name);
}
}
}
printf("\n\t\t\t\t\t ╚------------------------------------╝\n");
Prompt(b, "返回", "退出", Menu);
}
//菜单函数
void Menu(CSTree T) {
system("cls");
//菜单界面
printf("\t\t\t\t\t\t\t$ 家谱管理系统 $\n\n");
printf("\t\t\t\t\t\t\t1 # 显示家谱成员\n");
printf("\t\t\t\t\t\t\t2 # 查找家谱成员\n");
printf("\t\t\t\t\t\t\t3 # 修改家谱成员\n");
printf("\t\t\t\t\t\t\t4 # 添加家谱成员\n");
printf("\t\t\t\t\t\t\t5 # 删除家谱成员\n");
printf("\t\t\t\t\t\t\t6 # 统计家谱成员\n");
printf("\t\t\t\t\t\t\t7 # 家谱成员关系\n");
printf("\t\t\t\t\t\t\t8 # 显示关系成员\n");
printf("\t\t\t\t\t\t\t9 # 显示家谱树\n");
printf("\t\t\t\t\t\t\t0 # 退出\n\n");
printf("\t\t\t\t\t\t╚-----------------------------╝\n");
//菜单选项
printf("请输入您要选择的功能:");
char choice = '0';
rewind(stdin);
scanf_s("%c", &choice, 1);
switch (choice)
{
case '1': ShowFamilyMembers(T);
break;
case '2':SearchFamilyMember(T);
break;
case '3':CascadingMenu(T);
break;
case '4':AddMember(T);
break;
case '5':DeleteMember(T);
break;
case '6':CoutMembers(T);
break;
case '7':Members_Relationship(T);
break;
case '8':InquireRelationship(T);
break;
case '9':PrintAsTree(T);
break;
case '0':return;
break;
default: {printf("输入有误,请重新输入!!!\n"); Menu(T); };
break;
}
return;
}
main.cpp
#include <stdio.h>
#include<stdlib.h>
#include<stdarg.h>
#include<math.h>
#include<string.h>
#include<time.h>
//公共配置
#include "common.h"
//辅助栈
#include "LStack.h";
//辅助队列
#include "SqQueue.h"
//孩子兄弟树操作
#include "CSTreeOperate.h"
//家谱管理系统操作
#include "GenealogyOperate.h";
int main()
{
//创建家谱树
CSTree T;
CreateFamilyTree(T);
Menu(T);
return 0;
}
data.txt
张成荣 男 1879 1959
张雨连 男 1900 1980
张建福 男 1925 2010
张炬彬 男 1950 -2
张炬海 男 1952 -2
张顺源 男 1977 -2
张秉梁 男 2000 -2
张建禄 男 1929 2015
张炬科 男 1949 -2
张长源 男 1980 -2
张建真 男 1930 2010
张建祥 男 1925 2005
张炬旺 男 1952 -2
张涛源 男 1985 -2
张雨兴 男 1900 1969
张雨文 男 1910 2000
张建温 男 1935 2000
张建成 男 1936 2005
张炬法 男 1955 -2
张军源 男 1988 -2
张秉轩 男 2005 -2
张杨源 男 1975 -2
张炬琴 女 1957 -2
张凯源 男 1977 -2
张建旺 男 1940 2020
张炬永 男 1965 -2
张炬胜 男 1970 -2
张瑞源 男 1995 -2
张炬凡 女 1968 -2
张辉源 男 1998 -2
张炬其 男 1973 -2
张炬珂 女 1974 -2
张炬杰 男 1980 -2
张建有 男 1935 2005
张炬震 男 1960 -2
张桐源 男 1965 -2
张炬坤 男 1963 -2
张柏源 男 1968 -2
仿照网上的一些资源完成的课程设计,代码仍存在一些问题。
课程设计报告以及全部代码地址:码云