同姓氏中国人见面常说的一句话是“我们五百年前可能是一家”。从当前目录下的文件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。
从标准输入读入要查找的两个人的名字,两名字间也以一个空格分隔。
假设当前目录下in.txt文件内容为:
6
wangliang wangguoping wangguoan
wangguoping wangtian wangguang
wangguoan wangxiang wangsong
wangtian wangqinian NULL
wangxiang wangping NULL
wangsong wanglong NULL
从标准输入读取:
wangqinian wangliang
wangqinian wangliang 3
家谱中输入了六个人名及其孩子的人名,形成了“问题描述”中的家谱,要查找的两人是wangqinian和wangliang,wangliang比wangqinian高3辈。
假设当前目录下in.txt文件内容为:
6
wangliang wangguoping wangguoan
wangguoping wangtian wangguang
wangguoan wangxiang wangsong
wangtian wangqinian NULL
wangxiang wangping NULL
wangsong wanglong NULL
从标准输入读取:
wangping wanglong
wangguoan wangping 2
wangguoan wanglong 2
和样例1同样输入了一家谱,wangping和wanglong共同的最近祖先是wangguoan,该祖先与两人相差两辈。
该程序要求在家谱中查找判断两人的关系远近,提交程序文件名为find.c。
#include
#include
#include
#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;
}