题目:
某公司利用链表管理员工之间的领导关系,如上图所示。
每名员工用链表中的一个节点表示,每个节点有name、next、prev和lead四个属性。以“王总”节点为例:
name(王总): 员工姓名,每个员工的姓名唯一;
next:指向和王总同级的后一位员工;
prev:指向和王总同级的前一位员工;
lead:指向直接受王总领导的员工。每一位员工直接领导最多1名员工,最少0名员工。
请你实现一个系统,接受4种命令作为输入,实现4种功能:
1.(输入)init
(执行)按照上图中的组织关系构造链表。
(输出)链表构造完成,现有x名员工
(说明)x为当前链表中的员工数目,这里应为9。
2.(输入)list
(执行)利用递归函数,从链表头节点(胡总)开始,“访问”每个节点。
定义“访问”:首先输出当前节点的name。如果当前节点既领导员工,又有后一位员工,则先“访问”当前节点领导的员工,再“访问”后一位员工;如果当前节点只领导员工,则“访问”当前节点领导的员工;如果当前节点只有后一位员工,则“访问”当前节点的后一位员工。
(输出)胡总 王总 张三 李四 小彭 小陈 老六 张总 黎总
(说明)首先先访问胡总,输出“胡总”;胡总没领导员工,访问后一名员工,王总;
输出“王总”,王总领导张三,访问张三;
输出“张三”,张三没领导员工,访问后一名员工,李四;
输出“李四”,李四领导小彭,访问小彭;
输出“小彭”,小彭没有领导员工,访问后一名员工,小陈。
输出“小陈”,小陈没领导员工,也无后一名员工,返回到李四,访问李四的后一名员工,老六。
输出“老六”,老六没领导员工,也无后一名员工,返回到王总,访问王总的后一名员工,张总。
输出“张总”,张总没领导员工,访问后一名员工,黎总。
输出“黎总”,黎总没领导员工,也无后一名员工,结束。
3.(输入)[参数1] lead by [参数2]
(执行)插入一个新节点,name为[参数1];令[参数2]节点的lead指针指向该新节点;插入完成后执行“list”命令。
(输出为list命令的输出)
(说明)保证[参数1]不与已有的name重复;保证[参数2]存在于链表中,且[参数2]节点没有领导员工。
4.(输入)[参数1] follow [参数2]
(执行)插入一个新节点,name为[参数1];令[参数2]节点的next指针指向[参数1]节点,[参数1]节点的prev指针指向[参数2]节点;插入完成后执行“list”命令。
(输出为list命令的输出)
(说明)保证[参数1]不与已有的name重复;保证[参数2]存在于链表中,且[参数2]节点没有后一个员工。
样例
(输入)init
(输出)链表构造完成,现有9名员工
(输入)list
(输出)胡总 王总 张三 李四 小彭 小陈 老六 张总 黎总
(输入)小张 follow 小陈
(输出)胡总 王总 张三 李四 小彭 小陈 小张 老六 张总 黎总
(输入)小卢 lead by 小陈
(输出)胡总 王总 张三 李四 小彭 小陈 小卢 小张 老六 张总 黎总
一、实验内容
公司组织管理:
某公司利用链式结构存储和管理员工之间的关系,每一名员工使用一个结点表示,并且含有四个属性name、next、prev和lead。
本实验要求实现一个系统,接收四种命令作为输入,实现四种功能:
1.init初始化
构造链表并且打印出员工的个数。
2.list遍历访问
使用递归函数访问每一个结点
3.加入lead元素
使用命令“[参数1] lead by [参数2]”加入元素,使参数2的元素领导参数1的元素
4.加入follow元素
使用“[参数1] follow [参数2]”命令加入元素,使参数2的元素跟在参数1 的元素后面。
二、实验过程及结果
使用结构体定义结点,每个结点有四个属性:
使用typedef定义node类型。
(一)、init初始化
初始化过程中,我将录入题目中所给图片的每个结点之间的链表关系、每个结点的元素属性(如姓名)。但是由于我们的head结点在之后经常用到,为了防止在遍历过程更改,我使用静态变量huzong。
对于init初始化链表之后,我将使用:
printf("链表构造完成,现有%d名员工\n", 9+flag); 输出
其中的flag是加入的结点数量,在刚开始的时候是0,每次加入一个结点之后会增加1。
Flag的定义(静态变量):
执行init之后的程序表现:
(二)、list遍历访问
使用递归函数对链表中的各个节点按照题目中的要求进行访问。
注意:要判断每个结点是否为NULL,否则调用head->next之类的时候,程序可能会报错;另外还要判断接下来要访问的next 或者lead是不是NULL,否则可能会打印处(null)字样。
程序执行结果:
(三)、加入lead元素
由结构[参数1] lead by [参数2],通过对于输入的字符串进行逐字符识别,识别出lead by 之后,会由该lead by的位置,找到参数1和参数2。
此时通过遍历原链表,找到name为参数2的结点,并构造一个新的结点,让该结点的name为参数1。
之后,我们将name为参数2的结点的lead指针指向新构造的结点。
由此,我们就实现了[参数1] lead by [参数2]的操作。
接下来调用list函数,实现对于列表中元素的遍历打印就好了。
程序执行效果展示:
(四)、加入follow元素
由结构[参数1] follow [参数2],通过对于输入的字符串进行逐字符识别,识别出follow 之后,会由该follow的位置,找到参数1和参数2。
此时通过遍历原链表,找到name为参数2的结点,并构造一个新的结点,让该结点的name为参数1。
之后,我们使name为参数2的结点的next指向新构建的结点,并且使新构建的结点的prev指向name为参数2 的结点。
由此我们就实现了follow的元素关系的链表构建。
接下来我们只需调用list函数,遍历并打印出链表的name信息就可以了。
程序执行效果展示:
三、实验心得
本次实验遇到诸多bug,让我感到不是在写code,更多的是在写bug。接下来我将讲述我的问题经历以及解决方案。
1.对于指针是否为NULL的判断缺失
在含有指针编程的时候,一定都要考虑到万一指针为NULL的情形,就如我在编写
void list(node* head)
{
printf("%s ", head->name);
if(head != NULL)
if(head->lead != NULL)
list(head->lead);
if(head != NULL)
if(head->next != NULL)
list(head->next);
}
一开始并没有进行指针非空的判断,因此执行到该处时,程序都会崩溃。
另外head->lead != NULL 的判断也是必要的,虽然没有这个不会导致程序崩溃,但是经过list的遍历打印之后,链表中会出现很多(null)的结点,因此需要加上对于结点各属性指针的判空,才能打印出需要的list序列。
2.对于递归的理解不足
在下面的程序中,需要使用递归的方式:
我发现需要对于每层递归中find函数的值进行返回,
因为每次返回实际上只是返回到了上一层,如果只是在最后一层找到之后返回我想要的head,是不会返回到最上面一层的,只有通过每一层的a = find(…,…)的返回,才能够在main函数调用find()的时候得到返回值。
而反观遍历函数不需要返回是因为,我只是需要它打印出每个值即可,并不需要这个函数给出一个返回值。
node* find(node* head, char* ex_name) //递归的构造(返回),并且需要判断条件。
{
node* a = NULL;
int i = 1;
if(head!=NULL)
{
i = strcmp((head->name), ex_name);
}
if(i==0)
{
return head;
}
if(head->lead!=NULL)
a = find(head->lead, ex_name);
if(a!=NULL)
{
return a;
}
if(head->next!=NULL)
a = find(head->next, ex_name);
if(a!=NULL)
{
return a;
}
}
3.对于string初始化的问题
由于我们需要判定某个string中是否含有某个值,那么就需要string初始为空字符串,否则在匹配字符的时候可能不会找到结尾的’\0’。
有几种方式
在初始化时初始为””或者”a”(这样除了第一位之外全部为\0)
另外还有一种方式:在实例化的时候使用memset()。
而切忌使用strcpy,因为这样无法将初始化之后的string的后面置空。
另外,在自己使用完一个字符串之后,如果想要重复使用,一定要先使用memset()进行置空操作。
另外,在结构体中,不能够初始化字符串,例如:
问题具体描述:c - I get an error when I initialize a string in a structure - Stack Overflow
解决的方案就是使用类与对象的方式初始化字符串,或者是在我进行实例化的时候逐个对于结点的name属性使用memeset()操作进行置空。
实验全部代码:
#include <stdio.h>
#include <string.h>
#include <malloc.h>
typedef struct node
{
char name[50];
struct node* next;
struct node* prev;
struct node* lead;
} node;
static int flag = 0;
static node* nodes[100];
static int q=0;
static node* huzong;
node* init()
{
node* wangzong = (node*)malloc(sizeof(node));
node* zhangzong = (node*)malloc(sizeof(node));
node* lizong = (node*)malloc(sizeof(node));
node* zhangsan = (node*)malloc(sizeof(node));
node* lisi = (node*)malloc(sizeof(node));
node* laoliu = (node*)malloc(sizeof(node));
node* xiaopeng = (node*)malloc(sizeof(node));
node* xiaochen = (node*)malloc(sizeof(node));
strcpy(huzong->name, "胡总");
huzong->lead = NULL;
huzong->prev = NULL;
huzong->next = wangzong;
strcpy(wangzong->name, "王总");
wangzong->lead = zhangsan;
wangzong->prev = huzong;
wangzong->next = zhangzong;
strcpy(zhangzong->name, "张总");
zhangzong->lead = NULL;
zhangzong->prev = wangzong;
zhangzong->next = lizong;
strcpy(lizong->name, "黎总");
lizong->lead = NULL;
lizong->prev = zhangzong;
lizong->next = NULL;
strcpy(zhangsan->name, "张三");
zhangsan->lead = NULL;
zhangsan->prev = NULL;
zhangsan->next = lisi;
strcpy(lisi->name, "李四");
lisi->lead = xiaopeng;
lisi->prev = zhangsan;
lisi->next = laoliu;
strcpy(laoliu->name, "老六");
laoliu->lead = NULL;
laoliu->prev = lisi;
laoliu->next = NULL;
strcpy(xiaopeng->name, "小彭");
xiaopeng->lead = NULL;
xiaopeng->prev = NULL;
xiaopeng->next = xiaochen;
strcpy(xiaochen->name, "小陈");
xiaochen->lead = NULL;
xiaochen->prev = xiaopeng;
xiaochen->next = NULL;
return huzong;
}
void list(node* head)
{
printf("%s ", head->name);
if(head != NULL)
if(head->lead != NULL)
list(head->lead);
if(head != NULL)
if(head->next != NULL)
list(head->next);
}
node* find(node* head, char* ex_name) //递归的构造(返回),并且需要判断条件。
{
node* a = NULL;
int i = 1;
if(head!=NULL)
{
i = strcmp((head->name), ex_name);
}
if(i==0)
{
return head;
}
if(head->lead!=NULL)
a = find(head->lead, ex_name);
if(a!=NULL)
{
return a;
}
if(head->next!=NULL)
a = find(head->next, ex_name);
if(a!=NULL)
{
return a;
}
}
void lead(node* new_node, node* existing)
{
existing->lead = new_node;
new_node->lead = NULL;
new_node->next = NULL;
new_node->prev = NULL;
}
void follow(node* new_node, node* existing)
{
existing->next = new_node;
new_node->prev = NULL;
new_node->lead = NULL;
new_node->next = NULL;
}
int main()
{
huzong = (node*)malloc(sizeof(node));
for(q = 0; q<100; q++)
{
nodes[q] = (node*)malloc(sizeof(node));
nodes[q]->lead = NULL;
memset(nodes[q]->name, 0x00, sizeof(char)*50);
nodes[q]->next = NULL;
nodes[q]->prev = NULL;
}
char input[100]="a";
char new_name[50]="a";
char ex_name[50]="a";
node* head;
node* temp = NULL;
head = init();
while(1)
{
head = huzong;
memset(input, 0x00, sizeof (char) * 100);// memset的用法好好用
gets(input);
for(int i = 0; i < 50; i++)
{
//init初始化实现
if(input[i]=='i'&&input[i+1]=='n'&&input[i+2]=='i'
&&input[i+3]=='t')
{
init();
printf("链表构造完成,现有%d名员工\n", 9+flag);
}
//list遍历功能实现
if(input[i]=='l'&&input[i+1]=='i'&&input[i+2]=='s'
&&input[i+3]=='t')
{
list(head);
printf("\n");
}
//lead功能实现
if(input[i]=='l'&&input[i+1]=='e'&&input[i+2]=='a'
&&input[i+3]=='d'&&input[i+5]=='b'&&input[i+6]=='y')
{
for(int j=0; j<=i-2; j++)
{
new_name[j] = input[j];
}
for(int k=i+8; k<=i+25; k++)
{
ex_name[k-i-8] = input[k]; //如果x_name溢出,将会污染到newname的位置
}
//node* new_node = (node*)malloc(sizeof(node));
strcpy(nodes[flag]->name, new_name);
temp = find(head, ex_name);
temp->lead = nodes[flag];
nodes[flag]->lead = NULL;
nodes[flag]->next = NULL;
nodes[flag]->prev = NULL;
list(head);
printf("\n");
flag++;
}
if(input[i]=='f'&&input[i+1]=='o'&&input[i+2]=='l'
&&input[i+3]=='l'&&input[i+4]=='o'&&input[i+5]=='w')
{
memset(new_name, 0x00, sizeof(char)*50);
for(int j=0; j<=i-2; j++)
{
new_name[j] = input[j];
}
for(int k=i+7; k<=i+30; k++)
{
ex_name[k-i-7] = input[k]; //如果ex_name溢出,将会污染到newname的位置
}
strcpy(nodes[flag]->name, new_name);
temp = find(head, ex_name);
temp->next = nodes[flag];
nodes[flag]->lead = NULL;
nodes[flag]->next = NULL;
nodes[flag]->prev = temp;
list(head);
printf("\n");
flag++;
}
}
}
}