您的位置:首页 > 理论基础 > 数据结构算法

<数据结构>孩子兄弟表示法家谱

2013-06-04 21:22 405 查看
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
const int MAXSIZE = 5000;
const int NAME_LENGTH = 500;

using namespace std;

struct TreeNode
{
char name[NAME_LENGTH];//定义一个字符数组,存放姓名
int level;//辈分
struct TreeNode *child;//第一个孩子
struct TreeNode *brother;//兄弟
};

//加入队列,把每个人的信息存进去
struct QueueNode
{
char name[NAME_LENGTH];
int level;//辈分
struct QueueNode *next;
};

QueueNode *q = NULL, *qt, *newq;//定义一个队列的全局变量

//载入文件
void Load(char str[])
{
FILE *fp;
if ((fp = fopen("jiapu_by_pt.txt", "r")) == NULL)
{
cout << "Can't open file!\n";
exit (0);
}

fgets(str, MAXSIZE, fp);//从文件中读取一行字符串到str中
fclose(fp);//关闭文件
}

//创建树,根据嵌套括号表示法的字符串*str生成树
TreeNode *CreateTree(TreeNode *b, char *str)
{
TreeNode *stack[MAXSIZE];//声明一个栈指针
TreeNode *p;//临时指针
int iName, stName; //记录名字的长度,记录名字起始的下标
int k;//判断是孩子还是兄弟的变量
int j = 0;//str的下标
int i, m;
int top = -1;//栈顶变量
int tLevel = 1;//存放零时辈分
char ch;//存放字符的零时变量
char tName[NAME_LENGTH];//用来取出名字用
b = NULL;
ch = str[j];

//循环遍历整个字符串
while (ch != '\0')
{
switch (ch)
{
case '(': top++; stack[top] = p; k=1; tLevel++; break; // 为孩子,辈分加1
case ')': top--; break;//辈分减1
case ',': k=2 ; tLevel--; break;//为兄弟,辈分不变
default: p = new TreeNode;//动态为其分配内存空间,把名字起始下标给stName变量
stName  = j;
iName = 0;//将名字的长度置位0
//循环计算名字的长度
while (ch!='\0' && ch!='(' && ch!=',' && ch!=')')
{
iName++;
j++;
ch = str[j];
}
j--;//由于最后j要自加,所以就跳过了以上循环不满足的四种情况,此时要让k自减

m = 0;
//取出名字的字符串
for (i=stName; i<stName+iName; i++)
{
tName[m++] = str[i];
}
tName[m] = '\0';
//printf("%s\n", tName);

strcpy(p->name, tName);//赋值姓名
p->level = tLevel;//赋值辈分
p->child = p->brother = NULL;//让p的孩子节点,兄弟节点为空

if (b == NULL)
b = p;//根节点
else
{
switch (k)
{
case 1: stack[top]->child = p; break;//k为1代表了孩子节点
case 2: stack[top]->brother = p; break;//k为2代表了兄弟节点
}
}
}
j++;
ch = str[j];
}
return b;
}

//输出指定家庭的所有成员
TreeNode *Find(TreeNode *b, char inName[])
{
TreeNode *p;//定义一个接受查找的指针变量
if (b == NULL)//树空
return NULL;
else if (!strcmp(b->name, inName))//查找的为根节点
return b;
else
{
p = Find(b->child, inName);//递归查找孩子
if (p!=NULL)//如果查找到了
return p;
else return Find(b->brother, inName);//递归查找兄弟
}
}

//输出指定成员的辈数
int GetLevel(TreeNode *b, char inName[])
{
TreeNode *p;
p = Find(b, inName);//调用查找姓名为inName的孩子
if (p == NULL)
return 0;
else
return p->level;//直接返回它的辈份变量即可
}

//把家庭的存储结构写入文件中
char s[2]="(",p[2]=",",t[2]=")";
void Save(TreeNode *b,FILE *fp)
{
//如果b不为空树
if(b!=NULL)
{
fputs(b->name, fp);//将名字写入文件

//如果孩子或者兄弟不为空
if(b->child!=NULL || b->brother!=NULL)
{
//printf("(");
fputs(s,fp);//将左括号写入文件

Save(b->child, fp);//递归孩子节点写入文件,一直到空

if (b->brother!=NULL)
{
// printf(",");
fputs(p,fp);//将逗号写入文件
}

Save(b->brother,fp);//递归兄弟节点写入文件,一直到空
//printf(")");
fputs(t,fp);//将又括号写入文件
}
}
}

//在家庭中添加新成员
void Add(TreeNode *b,char par[],char chi[])
{
FILE *fp;//定义一个文件指针
TreeNode *p,*q;//树节点p,q
p = Find(b, par);//找到双亲节点的指针
if (p==NULL)
{
cout << "\n你要添加孩子的双亲不存在,添加失败!!!!\n\n";
return ;
}
q = new TreeNode;//为q分配内存空间
strcpy(q->name, chi);//复制孩子的姓名到新节点的姓名
q->level = p->level+1;//孩子的辈份在双亲的基础上加1
q->child=q->brother=NULL;
if(p->child == NULL)//如果孩子的节点为空
{
p->child = q;//直接把q当成他的孩子
}
else
{
p=p->child;//找到他的孩子
while(p->brother!=NULL)//循环到孩子的最后一个兄弟
{
p=p->brother ;
}
p->brother=q;//把q当成他的兄弟
}
cout << "向文件中写入新家谱......\n";
if((fp = fopen("jiapu_by_pt.txt","w"))==NULL)
{
cout << "\n can't open the file.\n";
exit(0);
}
else
Save(b,fp);//保存问年
fclose (fp);//关闭文件
}

//初始化队列
void QueueInit(TreeNode *b)
{
if (b!=NULL)
{
if (q==NULL)
{
q = new QueueNode;//为第一个队列节点分配内存
strcpy(q->name, b->name);//将名字复制到第一个队列节点
q->level = b->level;//复制辈份
q->next = NULL;
qt = q;//让qt临时指针变量指向第一个队列节点
}
else
{
newq = new QueueNode;//为非第一个队列节点分配内存
newq->next = NULL;
strcpy(newq->name, b->name);//将名字复制到非第一个队列节点
newq->level = b->level;//复制辈份
qt->next = newq;//将其连接起来
qt = newq;//让qt临时指针变量指向新创建的队列节点(即最后一个队列节点)
}
QueueInit(b->child);
QueueInit(b->brother);
}
}

//使用嵌套括号表示法输出
void PrintTree(TreeNode *b)
{
if (b!=NULL)//如果树不为空
{
cout << b->name;//打印根节点的姓名
if (b->child!=NULL || b->brother!=NULL)//孩子或者兄弟不为空
{
cout << "(";
PrintTree(b->child);//递归打印孩子
if (b->brother!=NULL)//如果兄弟不为空
cout << ",";
PrintTree(b->brother);//递归打印兄弟
cout << ")";
}
}
}

//以凹入表表示法输出
void DispTree(TreeNode *b)
{
TreeNode *stack[MAXSIZE];//定义一个栈指针
TreeNode *p;
//level[m][0]代表的是打印stack[m]这个节点信息,打印多少个空格
//level[m][1]代表的是打印stack[m]这个节点信息,标示是孩子还是兄弟
//0表示孩子,1表示兄弟,2表示根
int level[MAXSIZE][2], top, n, i, width = 4;
char type;//用来显示的时候标识是孩子还是兄弟
if (b!=NULL)//树不为空时
{
top = 1;
stack[top] = b; // 根节点入栈
level[top][0] = width;//让根节点前面打印width个空格
level[top][1] = 2;//2表示根
//栈不为空
while (top > 0)
{
p = stack[top]; // 退栈并凹入显示该节点值
n = level[top][0];//把打印多少个空格的值赋值给n
//分支语句判断是孩子节点还是根节点
switch (level[top][1])
{
case 0: type = 'h'; break;//孩子节点
case 1: type = 'x'; break;//兄弟节点
case 2: type = 'g'; break;//根节点
}

//循环打印n歌空格
for (i=1; i<=n; i++)
cout << " ";
cout << p->name << "(" << type << ")\n";//输出名字和标示符
top--;//出栈

//如果兄弟节点不为空
if (p->brother!=NULL)
{
//将兄弟节点入栈
top++;
stack[top] = p->brother;
level[top][0] = n;//在n的基础上空格数+width个
level[top][1] = 1;//兄弟标示为1
}

//如果孩子节点不为空
if (p->child!=NULL)
{
//将孩子节点入栈
top++;
stack[top] = p->child;
level[top][0] = n+width;//在n的基础上空格数+width个
level[top][1] = 0;//孩子标示为0
}
}
}
}

//输出选择
void PrintSel(TreeNode *b)
{
int sel;
cout << "~~~~~~~~~~~~~~~~~~~~~~~\n";
cout << "1.凹入表表示法输出        \n";
cout << "2.嵌套括号表示法输出   \n";
cout << "~~~~~~~~~~~~~~~~~~~~~~~\n";
while (1)
{
cout << "请输入你的选择:";
cin >> sel;//输入打印选择
if (sel == 1)
{
cout << "凹入表表示法输出如下:\n\n";
DispTree(b);
break;
}
else if (sel == 2)
{
cout << "嵌套括号表示法输出如下:\n\n";
PrintTree(b);
break;
}
else
{
cout << "你输入的选项不存在,请从新输入:";
}
}
}

//输出指定辈的成员
void PrintLevel(int n)
{
QueueNode *p;
int flag = 0;
//循环遍历队列
cout << "\n";
for (p=q; p!=NULL; p = p->next)
if (p->level == n)//如果辈份对应
{
cout << "-" << p->name << "-";//打印相应的名字
flag = 1;
}

if (flag == 0)//如果标示为0,即不存在这个人
{
cout << "\n该家谱中不存在辈份为" << n << "的人!!!\n\n";
return ;
}
}

int Menu()
{
int select;
cout << "|**************欢迎进入蒲氏家谱系统**********|\n";
cout << "|       1.在家谱中添加新成员并追加到文件中   |\n";
cout << "|       2.输出指定家庭的所有成员             |\n";
cout << "|       3.打印出指定成员在家族中的辈份       |\n";
cout << "|       4.输出指定辈的所有成员               |\n";
cout << "|       5.输出整个家谱                       |\n";
cout << "|       6.退出                               |\n";
cout << "|********************************************|\n";
cout << "|参考文献:1.<<程序设计题典>>--清华大学出版社 |\n";
cout << "|         2.<<c程序设计>>--清华大学出版社    |\n";
cout << "|********************************************|\n";
cout << "|ps:计划生育说:少生优生 幸福一生   !!!!!!!!!|\n";
cout << "|********************************************|\n";
cout << "|作者:蒲通          *()*         日期:13.5.28|\n";
cout << "|********************************************|\n";
do
{
cout << "请输入你的选项:";
cin >> select;
}while (select<1 || select>6);
return select;
}

int main()
{
//char str[] = "蒲爷爷(蒲爸爸(蒲通(蒲通的大儿子,蒲通的二儿子),蒲弟弟),蒲姑父(蒲表哥))";
char str[MAXSIZE];
char par[NAME_LENGTH], chi[NAME_LENGTH];
Load(str);//载入文件,将字符串读入str数组中
TreeNode *b, *p;
b = CreateTree(b, str);
int sel, level;
char inName[NAME_LENGTH];
while (1)
{
sel = Menu();
switch (sel)
{
case 1:
getchar();//吃掉选择菜单回车符
cout << "输入你要添加孩子的双亲姓名:";
gets(par);//输入添加孩子的双亲姓名
cout << "输入你要添加孩子的姓名:";
gets(chi);//输入添加孩子的姓名
Add(b, par, chi);
//b = CreateTree(b, str);
break;
case 2:
cout << "请输入你要查找指定成员家庭的姓名:";
getchar();//吃掉选择菜单回车符
gets(inName);
p = Find(b, inName);
if (p == NULL)
{
cout << "\n你要查找指定成员家庭的姓名不存在!!!\n\n";
break;
}
cout << "\n" << inName << "(c)\n";
DispTree(p->child);//直接以凹入表表示法显示
cout << "\n\n";
break;
case 3:
cout << "请输入你要查找指定成员辈份的姓名:";
getchar();//吃掉选择菜单回车符
gets(inName);//输入查找指定成员辈份的姓名
level = GetLevel(b, inName);
if (level == 0)
{
cout << "\n你要查找指定成员辈份的姓名不存在,查找辈份失败!!!\n\n";
break;
}
cout << "\n" << inName << "是第" << level << "辈\n\n";
break;
case 4:
q = NULL;
QueueInit(b);
cout << "请输入要显示的辈份:";
cin >> level;
PrintLevel(level);
cout << "\n\n";
break;
case 5:
PrintSel(b);
cout << "\n\n";
break;
case 6:
cout << "\n谢谢使用^()^\n\n";
exit(0);
break;
}
}
return 0;
}

运行效果如下:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: