数据结构实战(十)——家谱管理系统

任务简述

实现具有下列功能的家谱管理系统。
基本要求:
(1)输入文件以存放最初家谱中各成员的信息,成员的信息中均应包含以下内容:姓名、出生日期、婚否、地址、健在否、死亡日期(若其已死亡),也可附加其它信息、但不是必需的。
(2)实现数据的文件存储和读取。
(3)以图形方式显示家谱。
(4)显示第n 代所有人的信息。
(5)按照姓名查询,输出成员信息(包括其本人、父亲、孩子的信息)。
(6)按照出生日期查询成员名单。
(7)输入两人姓名,确定其关系。
(8)某成员添加孩子。
(9)删除某成员(若其还有后代,则一并删除)。
(10)修改某成员信息。
(11)要求建立至少20个成员的数据,以较为直观的方式显示结果,并提供文稿形式以便检查。
(12)界面要求:有合理的提示,每个功能可以设立菜单,根据提示,可以完成相关的功能要求。
(13)存储结构:根据系统功能要求自行设计,但是要求相关数据要存储在数据文件中。测试数据:要求使用1、全部合法数据;2、局部非法数据。进行程序测试,以保证程序的稳定。

算法描述

用了树的数据结构,每个结点是家族中的一员以及他的配偶等信息,不考虑族内通婚的情况(不然关系不好判断)。若有数据异常则创建失败。成员若死亡,则除姓名外所有信息皆为0,这样方便操作。大部分函数用递归实现。用了图形以及非常精致的文字形式输出,效果很好。查找时家族成员与配偶分开查找,分别用SearchPedTree函数和SearchPedTree2函数进行,查找方法类似dfs,避免结点的重复查找或遗漏。查找关系时分两人是族内的和配偶共4种情况进行,是配偶时可考虑其配偶与另一人的关系。若两人相差4辈以上,则统称一人为另一人的祖先。删除时递归调用delbtall函数,考虑此人是族内的或者配偶两种情况,删除该点及其后代。界面十分合理,每一步都有非常清晰的提示。输出时用了opencv的函数以及自己写的函数(opencv无法输出中文),效果不错。
大部分操作都是在所有成员中查找,因此时间复杂度是O(n)
源代码

putText.h:
#ifndef PUTTEXT_H_
#define PUTTEXT_H_
 
#include <windows.h>
#include <string>
#include <opencv2/opencv.hpp>
 
using namespace cv;
 
void GetStringSize(HDC hDC, const char* str, int* w, int* h);
void putTextZH(Mat &dst, const char* str, Point org, Scalar color, int fontSize,
    const char *fn = "Arial", bool italic = false, bool underline = false);
 
#endif // PUTTEXT_H_
//共14行
putText.cpp:
#include "putText.h"
 
void GetStringSize(HDC hDC, const char* str, int* w, int* h)
{
   
    SIZE size;
    GetTextExtentPoint32A(hDC, str, strlen(str), &size);
    if (w != 0) *w = size.cx;
    if (h != 0) *h = size.cy;
}
 
void putTextZH(Mat &dst, const char* str, Point org, Scalar color, int fontSize, const char* fn, bool italic, bool underline)
{
   
    CV_Assert(dst.data != 0 && (dst.channels() == 1 || dst.channels() == 3));
 
    int x, y, r, b;
    if (org.x > dst.cols || org.y > dst.rows) return;
    x = org.x < 0 ? -org.x : 0;
    y = org.y < 0 ? -org.y : 0;
 
    LOGFONTA lf;
    lf.lfHeight = -fontSize;
    lf.lfWidth = 0;
    lf.lfEscapement = 0;
    lf.lfOrientation = 0;
    lf.lfWeight = 5;
    lf.lfItalic = italic;   //斜¡À体¬?
    lf.lfUnderline = underline; //下?划?线?
    lf.lfStrikeOut = 0;
    lf.lfCharSet = DEFAULT_CHARSET;
    lf.lfOutPrecision = 0;
    lf.lfClipPrecision = 0;
    lf.lfQuality = PROOF_QUALITY;
    lf.lfPitchAndFamily = 0;
    strcpy_s(lf.lfFaceName, fn);
 
    HFONT hf = CreateFontIndirectA(&lf);
    HDC hDC = CreateCompatibleDC(0);
    HFONT hOldFont = (HFONT)SelectObject(hDC, hf);
 
    int strBaseW = 0, strBaseH = 0;
    int singleRow = 0;
    char buf[1 << 12];
    strcpy_s(buf, str);
    char *bufT[1 << 12];  // 这a个?用®?于®¨²分¤?隔?字Á?符¤?串ä?后¨®剩º¡ê余®¨¤的Ì?字Á?符¤?,ê?可¨¦能¨¹会¨¢超?出?。¡ê
    //处ä|理¤¨ª多¨¤行D
    {
   
        int nnh = 0;
        int cw, ch;
 
        const char* ln = strtok_s(buf, "\n",bufT);
        while (ln != 0)
        {
   
            GetStringSize(hDC, ln, &cw, &ch);
            strBaseW = max(strBaseW, cw);
            strBaseH = max(strBaseH, ch);
 
            ln = strtok_s(0, "\n",bufT);
            nnh++;
        }
        singleRow = strBaseH;
        strBaseH *= nnh;
    }
 
    if (org.x + strBaseW < 0 || org.y + strBaseH < 0)
    {
   
        SelectObject(hDC, hOldFont);
        DeleteObject(hf);
        DeleteObject(hDC);
        return;
    }
 
    r = org.x + strBaseW > dst.cols ? dst.cols - org.x - 1 : strBaseW - 1;
    b = org.y + strBaseH > dst.rows ? dst.rows - org.y - 1 : strBaseH - 1;
    org.x = org.x < 0 ? 0 : org.x;
    org.y = org.y < 0 ? 0 : org.y;
 
    BITMAPINFO bmp = {
    0 };
    BITMAPINFOHEADER& bih = bmp.bmiHeader;
    int strDrawLineStep = strBaseW * 3 % 4 == 0 ? strBaseW * 3 : (strBaseW * 3 + 4 - ((strBaseW * 3) % 4));
 
    bih.biSize = sizeof(BITMAPINFOHEADER);
    bih.biWidth = strBaseW;
    bih.biHeight = strBaseH;
    bih.biPlanes = 1;
    bih.biBitCount = 24;
    bih.biCompression = BI_RGB;
    bih.biSizeImage = strBaseH * strDrawLineStep;
    bih.biClrUsed = 0;
    bih.biClrImportant = 0;
 
    void* pDibData = 0;
    HBITMAP hBmp = CreateDIBSection(hDC, &bmp, DIB_RGB_COLORS, &pDibData, 0, 0);
 
    CV_Assert(pDibData != 0);
    HBITMAP hOldBmp = (HBITMAP)SelectObject(hDC, hBmp);
 
    //color.val[2], color.val[1], color.val[0]
    SetTextColor(hDC, RGB(255, 255, 255));
    SetBkColor(hDC, 0);
    //SetStretchBltMode(hDC, COLORONCOLOR);
 
    strcpy_s(buf, str);
    const char* ln = strtok_s(buf, "\n",bufT);
    int outTextY = 0;
    while (ln != 0)
    {
   
        TextOutA(hDC, 0, outTextY, ln, strlen(ln));
        outTextY += singleRow;
        ln = strtok_s(0, "\n",bufT);
    }
    uchar* dstData = (uchar*)dst.data;
    int dstStep = dst.step / sizeof(dstData[0]);
    unsigned char* pImg = (unsigned char*)dst.data + org.x * dst.channels() + org.y * dstStep;
    unsigned char* pStr = (unsigned char*)pDibData + x * 3;
    for (int tty = y; tty <= b; ++tty)
    {
   
        unsigned char* subImg = pImg + (tty - y) * dstStep;
        unsigned char* subStr = pStr + (strBaseH - tty - 1) * strDrawLineStep;
        for (int ttx = x; ttx <= r; ++ttx)
        {
   
            for (int n = 0; n < dst.channels(); ++n){
   
                double vtxt = subStr[n] / 255.0;
                int cvv = vtxt * color.val[n] + (1 - vtxt) * subImg[n];
                subImg[n] = cvv > 255 ? 255 : (cvv < 0 ? 0 : cvv);
            }
 
            subStr += 3;
            subImg += dst.channels();
        }
    }
 
    SelectObject(hDC, hOldBmp);
    SelectObject(hDC, hOldFont);
    DeleteObject(hf);
    DeleteObject(hBmp);
    DeleteDC(hDC);
}//共137行

主程序:
#include<stdio.h> 
#include<stdlib.h> 
#include<string.h> 
#include<math.h>
#include<conio.h> 
#include "putText.h"
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
using namespace cv;
#define DAI 3   //记录该宗族的最新一代
#define Maxx 30

typedef struct{
    
	int num;//孩子个数 
	char name[Maxx];///姓名 
	int sex;//性别 
	char ldata[Maxx];//出生日期
	int alive; //是否死亡
	char pass[Maxx];//死亡日期
	char adress[Maxx];//地址
	char spouse[Maxx];//配偶的姓名
	char ldata1[Maxx];//出生日期
	int alive1; //是否死亡(1:活着,-1:死亡)
	char pass1[Maxx];//死亡日期
	int dai;  //第几代 
}ddataType;

typedef struct TNode{
    
	ddataType data;//数据域 
	struct TNode* nextstr[10];//指针域,指向孩子 
	struct TNode* parent;//指针域,指向父亲 
}PedTNode,*PedTree;//家谱树

void OutMenu();//输出家谱菜单 
void CreateNewPedTreeNode(PedTree Tree,int *k);//创建孩子,递归调用 
void CreatePedTree(PedTree Tree,int k);//创建Pedigree树 
void OutOneInfor(PedTree Tree);//递归调用实现输出整个家谱树 
void OutAllPedTree(PedTree Tree);//输出整个家谱信息 
int SearchPedTree(PedTree Tree,char Name[],PedTree* DrawTree);//在树中查找 
int SearchPedTree2(PedTree Tree,char Name[],PedTree* DrawTree);//在树中查找配偶是否匹配 
void ShowMenu1(PedTree Tree);//显示要查找人的信息 
void ShowMenu2();//显示添加信息的菜单 
int AddNewInfor(PedTree Tree);//添加新的家谱信息 
void AddChildInfor(PedTree Tree);//添加子女的信息 
void AddPouseInfor(PedTree Tree);//添加配偶信息 
void ChangeInfor(PedTree Tree);//修改家谱信息 
void ShowMenu3();//显示修改信息的菜单
void Seach_all_n(PedTree Tree,int aa);//输出所有n代成员 
void Seach_all_ldata(PedTree Tree,char *aa);//输出所有生日为aa的成员
void Seach_all_guanxi(PedTree Tree,char *name1,char *name2);//输出name1和name2的关系 
PedTree delbt(PedTree Tree,char *name);//删除掉名字为name的成员
void delbtall(PedTree Tree);//删除树 
void showchild(PedTree TempTree);//输出有关孩子的信息 
void show(PedTree T);//利用opencv的函数非递归遍历树并画图输出
void DrawLine(Mat img, Point start, Point end);//利用opencv的函数画线 
char Name[Maxx][Maxx],Spouse[Maxx][Maxx],Address[Maxx][Maxx],Idata[Maxx][Maxx],Idata1[Maxx][Maxx],Pass[Maxx][Maxx],Pass1[Maxx][Maxx];
int Alive[Maxx],Alive1[Maxx],Num[Maxx],Sex[Maxx],Dai[Maxx],Flag=0;

int main(){
   
	FILE *fp;
	char choice; 
	char s1[Maxx],s2[Maxx],s3[Maxx],s4[Maxx],s5[Maxx],s6[Maxx],s7[Maxx],ss[Maxx],name1[Maxx],name2[Maxx];
	int a,b,c,d,i=0,s,aa;
	system("color 2f");//背景色调为我最喜欢的绿色 
	PedTNode TreeNode; 
	PedTree Tree=&TreeNode; 
	int flag=0;//判断是否已经创建家谱 
	fp=fopen("家谱.txt","r");
	if(fp==NULL){
   
		printf("can not open file\n");
		exit(0);
	}
	while(fscanf(fp,"%s%s%d%s%s%s%d%d%s%s%d%d",s1,s2,&s,s3,s4,s5,&a,&b,s6,s7,&c,&d)!=EOF){
   //顺序为:姓名,配偶姓名,性别,地址,出生日期,配偶出生日期,是否死亡,配偶是否死亡,死亡日期,配偶死亡日期,子女数量,辈分 
		strcpy(Name[i],s1);
		strcpy(Spouse[i],s2);
		Sex[i]=s;
		strcpy(Address[i],s3);
		strcpy(Idata[i],s4);
		strcpy(Idata1[i],s5);
		Alive[i]=a;
		Alive1[i]=b;
		strcpy(Pass[i],s6);
		strcpy(Pass1[i],s7);
		Num[i]=c;
		Dai[i]=d;
		i++;
	}
	fclose(fp);
	while(1){
   	 
		fflush(stdin);//清除键盘缓冲区 
 		system("cls"); 
 		OutMenu(); 
 		printf("\n"); 
 		printf("\t\t请您选择:"); 
 		choice=getchar(); 
 		switch(choice){
    
 		case '1': 
  			if(!flag){
    
   				CreatePedTree(Tree,0);//创建Pedigree树 
   				flag=1; 
  			} 
  			else{
    
   				printf("\n\t\t家谱不能多次创建\n"); 
   				printf("\n\t\t按任意键继续:"); 
   				fflush(stdin); 
   				getch(); 
  			} 
  			break; 
 		case '2': 
  			if(flag) 
   				ShowMenu1(Tree);//显示要查找人的信息 
  			else{
    
   				printf("\n\t\t家谱未创建,请您先创建家谱\n"); 
   				printf("\n\t\t按任意键继续:"); 
   				fflush(stdin); 
   				getch(); 
  			} 
  			break; 
 		case '3': 
  			if(flag) 
   				AddNewInfor(Tree);//添加新的家谱信息 
  			else{
    
  				printf("\n\t\t家谱未创建,请您先创建家谱\n"); 
   				printf("\n\t\t按任意键继续:"); 
  				fflush(stdin); 
   				getch(); 
  			} 
  			break; 
 		case '4': 
  			if(flag) 
   				OutAllPedTree(Tree);//输出整个家谱信息 
  			else{
    
   				printf("\n\t\t家谱未创建,请您先创建家谱\n"); 
   				printf("\n\t\t按任意键继续:"); 
   				fflush(stdin); 
   				getch(); 
  			} 
  			break; 
 		case '5': 
  			if(flag) 
   				ChangeInfor(Tree);//修改家谱信息 
  			else{
    
   				printf("\n\t\t家谱未创建,请您先创建家谱\n"); 
   				printf("\n\t\t按任意键继续:"); 
   				fflush(stdin); 
   				getch(); 
  			} 
  			break;
  		case '6':
  			if(flag){
    
  				printf("\n\t\t请输入查找第几代的所有成员:");
  				scanf("%d",&aa);
  				if(aa<=DAI){
   
  					Seach_all_n(Tree,aa);  //输出所有aa代的成员 
				  }
  				else{
   
  					printf("\n\t\t越界,该宗族没有这一代!\n");
				  }
  				getch(); 
  			}
  			else{
   
  				printf("\n\t\t家谱未创建,请您先创建家谱\n"); 
   				printf("\n\t\t按任意键继续:"); 
   				fflush(stdin); 
   				getch();	
  			}
  			break;
 		case '7':
 			if(flag){
   
 				printf("\n\t\t请输入生日:");
  				scanf("%s",ss);
 				Seach_all_ldata(Tree,ss);
 				if(!Flag){
   
 					printf("\n\t\t这一天没有成员过生日\n");
				 }
 				getch();
 			}
 			else{
   
 				printf("\n\t\t家谱未创建,请您先创建家谱\n"); 
   				printf("\n\t\t按任意键继续:"); 
   				fflush(stdin); 
   				getch();	
 			}
 			break;
 		case '8':
 			if(flag){
   
 				printf("\n\t\t请输入第一个人的姓名:");
 				scanf("%s",name1);
 				printf("\n\t\t请输入第二个人的姓名:");
 				scanf("%s",name2);
 				Seach_all_guanxi(Tree,name1,name2);
 			}
 			else{
   
 				printf("\n\t\t家谱未创建,请您先创建家谱\n"); 
   				printf("\n\t\t按任意键继续:"); 
   				fflush(stdin); 
   				getch();	
 			}
 			break;
 		case '9':
 			if(flag){
   
 				printf("\n\t\t请输入待删除人员的姓名:");
 				scanf("%s",name1);
 				Tree=delbt(Tree,name1);//删除掉名字为name1的成员
 				getch();
 			}
 			else{
   
				printf("\n\t\t家谱未创建,请您先创建家谱\n"); 
   				printf("\n\t\t按任意键继续:"); 
   				fflush(stdin); 
   				getch();
			}
 			break;
		case '0':
  			return 0; 
 		} 
	} 
	return 0; 
}

void OutMenu() 
{
   
	printf("\t\t* * * * * * * * * * * * * * * * * * * * * * *\n"); 
	printf("\t\t*                                           *\n"); 
	printf("\t\t*         家谱信息管理菜单                 *\n"); 
	printf("\t\t*                                          *\n"); 
	printf("\t\t*          1:创建家谱信息                  *\n"); 
	printf("\t\t*          2:查找并输出某人信息            *\n"); 
	printf("\t\t*          3:添加新的家庭成员              *\n"); 
	printf("\t\t*          4:输出整个家谱信息              *\n"); 
	printf("\t\t*          5:修改某个人的信息              *\n");
	printf("\t\t*          6:输出第n代所有人的信息         *\n");
	printf("\t\t*          7:输出所有人生日为aa的信息      *\n");
	printf("\t\t*          8:输出两个人的关系              *\n");
	printf("\t\t*          9:删除名为name的成员            *\n"); 
	printf("\t\t*          0:退出整个程序                  *\n"); 
	printf("\t\t*                                           *\n"); 
	printf("\t\t* * * * * * * * * * * * * * * * * * * * * * *\n"); 
}

void CreatePedTree(PedTree Tree,int k){
   //创建Pedigree树 
	int i;
	fflush(stdin);//清除键盘缓冲区 
	system("cls"); 
	strcpy(Tree->data.name,Name[k]);
	Tree->data.sex=Sex[k];  
	strcpy(Tree->data.ldata,Idata[k]);
	Tree->data.alive=Alive[k];
	strcpy(Tree->data.pass,Pass[k]); 
	strcpy(Tree->data.adress,Address[k]); 
	Tree->data.num=Num[k]; 
	strcpy(Tree->data.spouse,Spouse[k]);
	strcpy(Tree->data.ldata1,Idata1[k]); 
	Tree->data.alive1=Alive1[k];
	strcpy(Tree->data.pass1,Pass[k]);
	Tree->data.dai=Dai[k];
	k++;
	if(strcmp(Tree->data.spouse,"0")==0 && Tree->data.num!=0){
    
 		printf("\t\t%s的配偶不存在,不能有孩子!\n",Tree->data.name); 
 		Tree->data.num=0;
		Tree->data.alive1=0;
		strcpy(Tree->data.pass1,"0");
		strcpy(Tree->data.ldata1,"0");
	} 
	Tree->parent=NULL; 
	for(i=0;i<Tree->data.num;i++){
    
 		printf("\n"); 
 		PedTree ChildTree=(PedTree)malloc(sizeof(PedTNode)); 
 		if(!ChildTree){
    
  			printf("内存不足!\n"); 
  			exit(-1); 
 		} 
 		Tree->nextstr[i]=ChildTree;//指向子女 
 		strcpy(ChildTree->data.name,Name[k]);
 		ChildTree->data.sex=Sex[k]; 
		strcpy(ChildTree->data.ldata,Idata[k]);
		ChildTree->data.alive=Alive[k];
		strcpy(ChildTree->data.pass,Pass[k]);
		strcpy(ChildTree->data.adress,Address[k]);
 		ChildTree->data.num=Num[k];
 		strcpy(ChildTree->data.spouse,Spouse[k]);
		strcpy(ChildTree->data.ldata1,Idata1[k]);
		ChildTree->data.alive1=Alive1[k];
		strcpy(ChildTree->data.pass1,Pass[k]);
		ChildTree->data.dai=Dai[k];
		k++;
 		if(strcmp(ChildTree->data.spouse,"0")==0 && ChildTree->data.num!=0) {
    
  			printf("\t\t%s的配偶不存在,不能有孩子!\n",ChildTree->data.name); 
  			ChildTree->data.num=0;
  			ChildTree->data.alive1=0;
			strcpy(ChildTree->data.pass1,"0");
			strcpy(ChildTree->data.ldata1,"0");
		} 
 		ChildTree->parent=Tree;//将孩子指向父母 
		CreateNewPedTreeNode(ChildTree,&k);
	} 
	fflush(
  • 24
    点赞
  • 194
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值