在家谱中查找关系远近

在家谱中查找关系远近

【问题描述】

同姓氏中国人见面常说的一句话是“我们五百年前可能是一家”。从当前目录下的文件in.txt中读入一家谱,从标准输入读入两个人的名字(两人的名字肯定会在家谱中出现),编程查找判断这两个人相差几辈,若同辈,还要查找两个人共同的最近祖先以及与他(她)们的关系远近。假设输入的家谱中每人最多有两个孩子,例如下图是根据输入形成的一个简单家谱:
家族树

通过该家谱,可以看到wangliang、wangguoping和wangguoan都有两个孩子,wangtian、wangxiang和wangsong有一个孩子,wangguang、wangqinian、wangping和wanglong还没有孩子。若要查找的两个人是wangqinian和wangguoan,从家谱中可以看出两人相差两辈;若要查找的两个人是wangping和wanglong,可以看出两人共同的最近祖先是wangguoan,和两人相差两辈。

【输入形式】

从当前目录下的in.txt中读入家谱。文件中第一行是家谱中有孩子的人数,后面每行内容是每个人的名字和其孩子的名字,名字都由1到20个英文字母构成,各名字间以一个空格分隔,整个家谱中的人员都不会重名;若只有一个孩子,则第二个孩子的名字为NULL;若没有孩子,则不需输入;输入的顺序是按照辈份从高到低依次输入,若孩子A出现在孩子B之前,则A的孩子应在B的孩子之前输入。假设以该形式读入的家谱肯定能够形成类似上图所示的一棵二叉树形式的家谱,家谱中任何两人相差的辈份不会超过100。

从标准输入读入要查找的两个人的名字,两名字间也以一个空格分隔。

【输出形式】

  • 所有信息输出到标准输出上。
  • 若要查找的两人不同辈,则先输出辈份低的名字,再输出辈份高的名字,然后输出相差几辈,都以一个空格分隔;
  • 若两人同辈,按照两人名字从标准输入读取的先后顺序,分行输出两人的最近祖先名字、两人姓名以及相差几辈,各数据间以一个空格分隔。

【样例1输入】

假设当前目录下in.txt文件内容为:

6
wangliang wangguoping wangguoan
wangguoping wangtian wangguang
wangguoan wangxiang wangsong
wangtian wangqinian NULL
wangxiang wangping NULL
wangsong wanglong NULL

从标准输入读取:

wangqinian wangliang

【样例1输出】

wangqinian wangliang 3

【样例1说明】

家谱中输入了六个人名及其孩子的人名,形成了“问题描述”中的家谱,要查找的两人是wangqinian和wangliang,wangliang比wangqinian高3辈。

【样例2输入】

假设当前目录下in.txt文件内容为:

6
wangliang wangguoping wangguoan
wangguoping wangtian wangguang
wangguoan wangxiang wangsong
wangtian wangqinian NULL
wangxiang wangping NULL
wangsong wanglong NULL

从标准输入读取:

wangping wanglong

【样例2输出】

wangguoan wangping 2
wangguoan wanglong 2

【样例2说明】

和样例1同样输入了一家谱,wangping和wanglong共同的最近祖先是wangguoan,该祖先与两人相差两辈。

【评分标准】

该程序要求在家谱中查找判断两人的关系远近,提交程序文件名为find.c。

思路

  • 本题的目标是查询辈分差或者找到最小共同父辈,因此可以使用树的结构处理,每个节点通过存储树的层次来表示辈分,并为每个节点添加一个指针,记录下每个点的父节点,以便后续查找父节点。
  • 个人认为本题的难点在于创建这个树的结构,后续查询以及处理都是较为简单的。

代码

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define N 1000
#define LEN_NAME 30

typedef struct Fnode* FNode;
int pos = 1;
struct Fnode {
	char Name[LEN_NAME];   //人名
	FNode Parent, Lchild, Rchild;   //父节点及子节点
	int Degress;   //辈分,逆排
};

FNode init(FNode P, char name[])
{
	if (!strcmp(name, "NULL"))
		return NULL;
	FNode n = (FNode)malloc(sizeof(struct Fnode));
	n->Lchild = n->Rchild = NULL;
	n->Parent = P;
	n->Degress = P ? P->Degress + 1 : 1;  //辈分最大的记为1
	strcpy(n->Name, name);
	return n;
}

void getName(char str[], char name[3][LEN_NAME])
{
	int i = 0, j = 0, k = 0;
	char c = str[i++];
	while (c != '\n' && c != '\0') {
		if (c == ' ') {  //遇到空格重置,记录新的人名
			j++;
			k = 0;
		} 
		else name[j][k++] = c;
		c = str[i++];
	}
}

FNode search(FNode F, char name[])
{
	if (!F) return NULL;   //空节点
	else if(!strcmp(name, F->Name)) return F;    //已经查找到该名字对应的节点
	else return search(F->Lchild, name) ? search(F->Lchild, name) : search(F->Rchild, name);   //如果当前节点不符合,递归查询其子节点
}

FNode creatTree(FNode F, char data[N][N])
{
	FNode nodes[N];
	int pos_nodes = 0, i;
	nodes[pos_nodes++] = F;   //按层创建树,先将根节点存入
	for (i = 0; i < pos_nodes; i++) {
		char name[3][LEN_NAME] = { '\0' };
		getName(data[pos], name);
		if (!strcmp(nodes[i]->Name, name[0])) {    //解析到当前节点的信息,则为其创建子节点
			FNode l = init(nodes[i], name[1]), r = init(nodes[i], name[2]);
			nodes[i]->Lchild = l;
			nodes[i]->Rchild = r;
			if (l) nodes[pos_nodes++] = l;
			if (r) nodes[pos_nodes++] = r;
			pos++;
		}
	}
	return F;
}

void print(FNode F1, FNode F2)
{
	FNode f1 = F1, f2 = F2;
	if (f1->Degress > f2->Degress)    //两人辈分不同
		printf("%s %s %d", f1->Name, f2->Name, f1->Degress - f2->Degress);
	else if(f1->Degress < f2->Degress)
		printf("%s %s %d", f2->Name, f1->Name, f2->Degress - f1->Degress);
	else {   //两人辈分相同,查询其最小共同父代
		while (strcmp(f1->Name, f2->Name)) {
			f1 = f1->Parent;
			f2 = f2->Parent;
		}
		printf("%s %s %d\n", f2->Name, F1->Name, F1->Degress - f2->Degress);
		printf("%s %s %d", f2->Name, F2->Name, F2->Degress - f2->Degress);
	}
}

int main()
{
	int n = 0;
	char data[N][N] = { '\0' }, name[3][LEN_NAME] = { '\0' }, names[2][LEN_NAME];
	FILE* fp = fopen("in.txt", "r");
	while (!feof(fp))
		fgets(data[n++], 3 * LEN_NAME, fp);  //读取文本内容,含'\n'
	fclose(fp);
	getName(data[1], name);  //先获得辈分最大的人的姓名,创建根节点
	FNode F = init(NULL, name[0]);
	creatTree(F, data);   //创建整个树
	scanf("%s %s", names[0], names[1]);
	FNode F1 = search(F,names[0]), F2 = search(F, names[1]);  //找到两个人名对应的节点
	print(F1, F2);
	return 0;
}
  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

谛凌

本人水平有限,感谢您支持与指正

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

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

打赏作者

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

抵扣说明:

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

余额充值