/**
@孩子兄弟表示法建立的一般树
家谱的设计主要是实现对家庭成员信息的建立、查找、插入、修改、删除等功能。要求和基本功能如下
(1)采用两种存储结构实现家谱管理功能,其中一种是孩子兄弟链表存储结构。;
(2)家庭成员的添加:即添加某一人的儿女,儿女的数目由控制台端给出,然后输入相应的儿女姓名(此处儿女的姓名不能重名) 。
(3)家庭成员的修改:可以修改某一成员的姓名。
(4)成员的查询:查询某一成员在家族中的辈分(第几代) ,并能查询此成员的所有子女及这一辈的所有成员。
(5)家庭成员的删除:删除此成员时,若其有后代,将删除其所有后代成员
*/
#include<iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#define OK 1
#define ERROR 0
#define FALSE 0
#define TRUE 1
#define OVERFLOW -2
using namespace std;
typedef int Status;
typedef char ElemType; ///结点的值设置为字符
typedef struct CSNode {
ElemType data;
struct CSNode *firstChild; ///第一个孩子
struct CSNode *nextsbling; ///该孩子的下一个兄弟
} CSNode, *CSTree;
typedef CSTree QElemType;
typedef struct QNode {
QElemType data;
struct QNode *next;
} QNode,*QueuePtr;
typedef struct {
QueuePtr front; ///队头指针
QueuePtr rear; ///队尾指针
} LinkQueue;
Status InitQueue(LinkQueue &Q) { ///构造一个空队列
Q.front = Q.rear = (QueuePtr)malloc(sizeof(QNode));///队头结点
if(!Q.front)
exit(OVERFLOW);
Q.front ->next = NULL;
return OK;
}
Status QueueEmpty(const LinkQueue &Q) { ///若队列为空,则返回TRUE,否则返回FALSE
if(Q.rear == Q.front)
return TRUE;
return FALSE;
}
Status EnQueue(LinkQueue &Q, QElemType e) { ///插入元素e为Q的新队尾元素
QueuePtr p = (QueuePtr)malloc(sizeof(QNode));
if(!p)
exit(OVERFLOW);
p->data = e;
p->next = NULL;
Q.rear->next = p;
Q.rear = p;
return OK;
}
Status DeQueue(LinkQueue &Q,QElemType &e) { ///若队列不空,则删除Q的队头元素,用e返回其值,并返回OK;
if(Q.front == Q.rear) {
return ERROR; //队空
}
QueuePtr p = Q.front->next;
e = p->data;
Q.front->next = p->next;
if(Q.rear == p)
Q.rear = Q.front;
free(p);
return OK;
}
///创建一棵树
Status CreateTree(CSTree &T) { ///创建一棵树
LinkQueue Q;
InitQueue(Q);///构造一个空队列
char buffChild[20]; ///用于存放孩子们的缓存
memset(buffChild,0,20); ///初始化缓存数组,置为NULL
printf("请输入树的根结点(字符,以#代表空):\n");
scanf("%c",&buffChild[0]);
if(buffChild[0] != '#') {
T = (CSTree)malloc(sizeof(CSNode));///为根结点开辟一个空间
if(!T)
exit(OVERFLOW); ///开辟失败,终止程序
T->data = buffChild[0];
T->nextsbling = NULL; ///根结点无兄弟
EnQueue(Q,T); ///根结点入队
while(!QueueEmpty(Q)) {
QElemType e;
DeQueue(Q,e); ///结点出队
//CSTree p = e; //用以指向队头结点
printf("请输入结点%c的孩子(输入的字符串以#结束):\n",e->data);
scanf("%s",buffChild);
if(buffChild[0] != '#') { //有孩子
CSTree q;
q = (CSTree)malloc(sizeof(CSNode)); //开辟孩子结点空间
if(!q)
exit(OVERFLOW);
q->data = buffChild[0]; //
e->firstChild = q; ///指向第一个孩子
EnQueue(Q,q); ///第一个孩子入队
CSTree p = q; ///指向刚入队的孩子
for(size_t i = 1; i < strlen(buffChild)-1; ++i) { ///孩子存在兄弟
q = (CSTree)malloc(sizeof(CSNode)); ///开辟孩子结点空间
if(!q)
exit(OVERFLOW);
q->data = buffChild[i];
p->nextsbling = q;
EnQueue(Q,q); ///入队
p = q; ///指向刚入队的孩子
}
p->nextsbling = NULL;///最后一个没有后缀兄弟
} else { ///无孩子
e->firstChild = NULL;
}
}
} else {
T = NULL;//空树
}
return OK;
}
///是否是空树
Status TreeEmpty(const CSTree &T) {
if(T) ///从树T存在,空树返回TRUE,否则返回FALSE
return TRUE;
else
return FALSE;
}
///树的深度
int TreeDepth(const CSTree &T) {
//树T存在,返回树的深度
if(!T) { ///树空 家族无人
return 0;
}
if(!T->firstChild) { ///无孩子 只有他们那一代人
return 1;
}
CSTree p;
int depth,maxn = 0;
for(p = T->firstChild; p; ) {///往下找最深的一代
depth = TreeDepth(p);
if(depth > maxn)
maxn = depth;
p = p->nextsbling;
}
return maxn+1;//当前层的下一层
}
///得到姓名
ElemType Root(const CSTree &T) {
//树T存在,返回树的根
if(T)
return T->data;
return 0;
}
/// 查询结点
CSNode *FindNode(const CSTree &T,ElemType cur_e) {
///树T存在,返回值为cur_e的结点的指针
LinkQueue Q;
InitQueue(Q); ///构造一个空队列
if(T) {
EnQueue(Q,T);///树根入队
while(!QueueEmpty(Q)) {
QElemType e;
DeQueue(Q,e);
if(e->data == cur_e)
return e;
if(e->firstChild) { ///当前结点有长子,则该长子入队
EnQueue(Q,e->firstChild);
}
if(e->nextsbling) { ///当前结点有兄弟,则该兄弟入队
EnQueue(Q,e->nextsbling);
}
}
}
return NULL;
}
///修改
void Change(CSTree &T,ElemType cur_e,ElemType ans){
CSTree P=FindNode(T,cur_e);///找到位置
P->data=ans;
}
///找到双亲结点
CSNode *Parent(CSTree &T,ElemType cur_e) {
///初始条件:树T存在,cur_e是T中某个结点
///操作结果:若cur_e是T的非根结点,则返回它的双亲,否则返回空
LinkQueue Q;
InitQueue(Q);
if(T) {
if(T->data == cur_e)
return NULL;///根结点无双亲,结束,返回NULL
EnQueue(Q,T);///根结点入队
while(!QueueEmpty(Q)) {
QElemType e;
DeQueue(Q,e);
QElemType p = e;///提示刚出队的元素:
if(e->firstChild) { ///该结点有孩子
if(e->firstChild->data == cur_e) { ///或该孩子是所求的结点,则返回双亲
return p;
}
EnQueue(Q,e->firstChild);
QElemType brotherPtr = e->firstChild->nextsbling;///指向孩子的兄弟结点
while(brotherPtr) { ///该孩子有兄弟
if(brotherPtr->data == cur_e) { ///兄弟是所求的结点,则返回双亲
return p;
}
EnQueue(Q,brotherPtr);///兄弟入队
brotherPtr = brotherPtr->nextsbling;
}
}
}
}
return NULL;
}
///插入
ElemType RightSibling(CSTree &T, ElemType cur_e,ElemType son) {
///初始条件:树T存在,cur_e是T中的某个结点。
///操作结果:若cur_e有右兄弟,则返回它的右兄弟,否则返回空
CSTree q;
q = (CSTree)malloc(sizeof(CSNode)); //开辟孩子结点空间
if(!q)
exit(OVERFLOW);
q->data = son;
q->firstChild=NULL;
q->nextsbling=NULL;
CSNode *node;
node = FindNode(T,cur_e);///双亲结点
if(node->firstChild) {
node = node->firstChild; ///第一个孩子
while(node->nextsbling) { ///有右兄弟
node=node->nextsbling;///返回右兄弟的值
}
node->nextsbling = q; ///指向第一个孩子
} else {
node->firstChild=q;
}
}
///层序遍历树
Status LevelOrderTraverse(const CSTree &T) {
LinkQueue Q;
InitQueue(Q);
if(T) {
printf("%c ",T->data); ///访问结点
EnQueue(Q,T); ///根结点排队
while(!QueueEmpty(Q)) {
QElemType e,p;
DeQueue(Q,e);
p = e->firstChild;
while(p) {
printf("%c ",p->data);
EnQueue(Q,p);
p = p->nextsbling;
}
}
return OK;
}
return ERROR;
}
///删除
ElemType LeftChild(CSTree &T, ElemType cur_e) {
CSNode *node;
node = Parent(T,cur_e);
if(node->firstChild->data==cur_e){
node->firstChild=node->firstChild->nextsbling;
}
else {
node=node->firstChild;
while(node->nextsbling->data!=cur_e){
node=node->nextsbling;
}
node->nextsbling=node->nextsbling->nextsbling;
}
}
int main() {
///建立
CSTree T;
CreateTree(T);
printf("按层序遍历该树:");
LevelOrderTraverse(T);
printf("\n");
printf("树的根为: %c\n",Root(T));
printf("输入要查询的节点:\n");
ElemType e ;
cin>>e;
CSNode *node = FindNode(T,e);
if(node) {
printf("存在结点: %c\n",node->data);
}
printf("树的深度为: %d\n",TreeDepth(T));
node = Parent(T,e);
if(node) {
printf("结点%c的双亲是: %c\n",e,node->data);
} else {
printf("查询的节点不存在,或节点为根节点\n");
}
printf("添加:\n");
///添加
char y;
cin>>y;
RightSibling(T,e,y);
printf("按层序遍历该树:");
LevelOrderTraverse(T);
printf("\n");
cout<<"*********************"<<endl;
printf("删除:\n");
char ch;
cin>>ch;
///删除
LeftChild(T,ch);
printf("按层序遍历该树:");
LevelOrderTraverse(T);
printf("\n");
printf("修改:请输入要修改的字符\n");
cin>>ch;
printf("请输入修改后的结果:\n");
char ans;
cin>>ans;
Change(T,ch,ans);
LevelOrderTraverse(T);
return 0;
}
家谱 草稿
最新推荐文章于 2024-01-01 14:02:06 发布
该博客介绍了一个使用孩子兄弟链表存储结构实现的家谱管理系统,包括添加、修改、查询和删除家庭成员功能。系统允许添加成员、修改姓名、查询辈分和子女,并在删除成员时删除其所有后代。代码示例展示了如何通过C++实现这些操作,如层序遍历、查找双亲节点等。
摘要由CSDN通过智能技术生成