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

#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;
}

运行效果如下:


  • 8
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
很好,这是一道很有挑战性的数据结构题目。下面是我的解答: 1. 文件存储格式设计 我们可以将每个节点的信息存储为一个结构体,然后用链式存储结构来表示整个家谱树。在文件中,我们可以将每个节点的信息依次存储下来,每行表示一个节点,不同字段之间用空格隔开,每个节点的子节点也按照同样的格式存储在文件中。如下所示: ``` 张三 男 张媛 110101198001011234 40 1980-01-01 李四 女 李华 120101198101012345 10 2010-01-01 王五 男 王小丽 130101198201012345 8 2012-01-01 ``` 这个文件表示了一个家谱树,张三节点有两个子节点,分别是李四和王五,他们的信息也按照同样的格式存储在文件中。 2. 链表(孩子表示法)存储结构 我们可以定义一个节点结构体,包含所有的节点信息和一个指向子节点的指针,如下所示: ``` struct Node { string name; // 姓名 string gender; // 性别 string spouse; // 配偶姓名 string id; // 身份证号 int age; // 年龄 string birth; // 出生日期 Node *child; // 指向子节点的指针 }; ``` 然后,我们可以定义一个家谱树类,其中包含一个指向根节点的指针和一些基本操作: ``` class FamilyTree { public: FamilyTree(); // 构造函数 ~FamilyTree(); // 析构函数 void loadFromFile(string filename); // 从文件中加载家族信息 void saveToFile(string filename); // 将家族信息保存到文件中 void print(); // 输出家族信息 vector<Node*> findSons(string name); // 查找某人的所有儿子 vector<Node*> findParents(string name); // 查找某人的双亲 private: Node *root; // 指向根节点的指针 void clear(Node *node); // 递归删除节点 }; ``` 3. 相关操作的实现 loadFromFile() 函数的实现如下: ``` void FamilyTree::loadFromFile(string filename) { ifstream fin(filename); string line; stack<Node*> s; // 用栈来保存父节点 while (getline(fin, line)) { int depth = 0; // 记录当前节点的深度 while (line[depth] == ' ') depth++; // 计算空格数 Node *node = new Node; stringstream ss(line.substr(depth)); ss >> node->name >> node->gender >> node->spouse >> node->id >> node->age >> node->birth; node->child = NULL; if (depth == 0) { // 根节点 root = node; s.push(node); } else { while (depth <= s.size()) s.pop(); // 弹出栈顶元素直到找到父节点 Node *parent = s.top(); if (parent->child == NULL) { // 第一个子节点 parent->child = node; } else { // 后续子节点 Node *p = parent->child; while (p->child != NULL) p = p->child; p->child = node; } s.push(node); } } fin.close(); } ``` saveToFile() 函数的实现如下: ``` void FamilyTree::saveToFile(string filename) { ofstream fout(filename); queue<Node*> q; q.push(root); while (!q.empty()) { Node *node = q.front(); q.pop(); for (int i = 0; i < q.size(); i++) fout << " "; fout << node->name << " " << node->gender << " " << node->spouse << " " << node->id << " " << node->age << " " << node->birth << endl; Node *p = node->child; while (p != NULL) { q.push(p); p = p->child; } } fout.close(); } ``` print() 函数的实现如下: ``` void FamilyTree::print() { queue<Node*> q; q.push(root); while (!q.empty()) { Node *node = q.front(); q.pop(); cout << node->name << " " << node->gender << " " << node->spouse << " " << node->id << " " << node->age << " " << node->birth << endl; Node *p = node->child; while (p != NULL) { q.push(p); p = p->child; } } } ``` findSons() 函数的实现如下: ``` vector<Node*> FamilyTree::findSons(string name) { vector<Node*> sons; queue<Node*> q; q.push(root); while (!q.empty()) { Node *node = q.front(); q.pop(); Node *p = node->child; while (p != NULL) { if (p->gender == "男" && p->spouse == "" && node->name == name) { sons.push_back(p); } q.push(p); p = p->child; } } return sons; } ``` findParents() 函数的实现如下: ``` vector<Node*> FamilyTree::findParents(string name) { vector<Node*> parents; queue<Node*> q; q.push(root); while (!q.empty()) { Node *node = q.front(); q.pop(); Node *p = node->child; while (p != NULL) { if (p->name == name) { parents.push_back(node); } q.push(p); p = p->child; } } return parents; } ``` 4. 界面 界面可以用命令行实现,例如: ``` Welcome to family tree system! 1. Load family information from file 2. Save family information to file 3. Print family information 4. Find all sons of a person 5. Find all parents of a person 0. Quit Please enter your choice: 1 Please enter the filename: family.txt Load family information successfully! Please enter your choice: 3 张三 男 张媛 110101198001011234 40 1980-01-01 李四 女 李华 120101198101012345 10 2010-01-01 王五 男 王小丽 130101198201012345 8 2012-01-01 Please enter your choice: 4 Please enter the person's name: 张三 李四 女 李华 120101198101012345 10 2010-01-01 王五 男 王小丽 130101198201012345 8 2012-01-01 Please enter your choice: 5 Please enter the person's name: 李四 张三 男 张媛 110101198001011234 40 1980-01-01 Please enter your choice: 0 Goodbye! ``` 这样,我们就完成了这道数据结构题目的解答。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值