一、原理
如何写一个学生信息管理系统?那么首先想到,学生信息包含哪些东西(数据)。如:一个人,有名字、性别、年龄、专业、学号等信息,如何定义一个人?int,char,long等都不行,这时,结构体就是一个很好的办法。
定义一个结构体:
如: struct snode {
char name[16];
char sex;
char age;
long id;
.......
};
此时一个结构体就包含了一个学生的所有信息,但是学生不止一人,定义多个结构体吗?显然不现实,此时我们就需要在结构体里面加一个结构体成员,这个成员是结构体指针,这个指针指向下一个结构体,里面包含了学生信息和下一个的结构体指针,这样就可以通过上一个结构体找到下一个结构体,就像一块一块的东西用一根线挨个连接,每一段线就是一个指针,每一块东西就是结构体,它们整体就叫做链表,像一根链子一样,每个结构体就是它的节点。
如:struct snode {
char name[16];
char sex;
char age;
long id;
......
struct snode *next;
};
那么只要我们找到链子的头,就可以顺藤摸瓜,找到后面的东西,定义一个头节点,把学生信息都连在后面,此时我们就只需要知道头节点的地址,就可以通过遍历链表得到全部的学生信息。但是有一个问题,当学生人数少的时候,这是完全没有问题的,当学生人数上万或者跟多,遍历链表查找信息就太浪费时间了,其实有 一种快速查找的方法,那就是哈希(hash)算法。
hash算法:
首先要计算hash值,计算hash值的方法与很多种,可能每个人都有不同的计算方法,把hash值存入一个比较大的数组(结构体数组),如:arr[2048],&arr[hashval]就是它的地址,当做头地址,后面挂着相同hash值的学生信息,这些信息连成一个链表,找到hash值的地址就相当于找到链表的头了,遍历这个链表就能快速找到需要的信息。有点像图的链式存储。
原理:存入:根据关键字,计算hash值,存入该位置。
取出:根据关键字,计算hash值,直接取出来。
如下图:
![](https://img-blog.csdnimg.cn/img_convert/f6c9ccf7a4953dc4c1b07f6841b3e80d.png)
二、代码实现
代码的话,有几个不完美的地方:
由于使用名字计算hash值的,名字输入只能是英文字母。
可以存储同名的人,但是修改同名的人的其他数据(年龄、学号等),只能修改第一个人的信息,不能够指定人,可以增加一些判断来决定修改哪一个人的信息(代码中没有实现)。但是可先修改名字,找到这个新名字,再修改信息(年龄、学号等),然后再把名字改回去,或者删除后重新添加,不过都太麻烦了。
注:如果有大佬有更好的方法,请多多指教
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX 2048
struct stunode{
char name[16];
char sex[8];
int age;
int id;
struct stunode *next;
};
//定义一个结构体数组,用来存放链表头(hash值)
struct stunode stu_head_arr[2048];
//计算hash值,这里比较简单,由名字的字母计算,所以只能输入英文
int hash_calucate(char *name)
{
int sum=0;
for(int i=0;i<strlen(name);i++)
sum+=name[i];
return sum%MAX;//限制范围
}
//学生信息输入,每一个学生信息都是一个结构体
int stu_info_add_member(void)
{
struct stunode *p = malloc(sizeof(*p));
printf("pls input name sex age id:");
scanf("%s %s %d %d",p->name,p->sex,&p->age,&p->id);
int hashval=hash_calucate(p->name);//计算hash值
struct stunode *pthead=&stu_head_arr[hashval];//存入数组的值作为链表头
struct stunode *pttail=&stu_head_arr[hashval];
//头部插入
p->next=pthead->next;
pthead->next=p;
// 尾部插入
// while(pttail->next!=NULL)
// pttail=pttail->next;
// pttail->next=p;
return 0;
}
//学生信息查找,定义结构体函数
struct stunode *stu_info_find_member(char*name)
{
int hashval= hash_calucate(name);//计算hash值
struct stunode *pthead=&stu_head_arr[hashval];//找到对应的链表头
struct stunode *p=pthead->next;
while(p)//通过字符串比较,查找所需要的信息
{
if(strcmp(p->name,name)==0)
return p;
p=p->next;
}
return NULL;//没找到返回空
}
//修改学生信息
int stu_info_change(char*name)
{
struct stunode *p = stu_info_find_member(name);//通过查找函数找到地址
if(!p)
{
printf("sorry no found\n");
return -1;
}
int hashval=hash_calucate(p->name);//计算hash值
struct stunode *q=&stu_head_arr[hashval];//记录头地址
while(1)
{
printf("*****************菜单*****************\n");
printf("1: modify name\n");
printf("2: modify sex\n");
printf("3: modify age\n");
printf("4: modify id\n");
printf("5: exit\n");
printf("*****************菜单*****************\n");
int select;
scanf("%d",&select);
switch(select)
{
case 1:if(q->next!=p)//找到p前面的地址
q=q->next;
q->next=p->next;//q与p后面的数据相连
printf("pls modify name:");scanf("%s",p->name);
// q->next=p->next;//原来的头与p后面的数据相连
int val=hash_calucate(p->name);//计算hash值
struct stunode *pthead=&stu_head_arr[val];//存入数组的值作为新的链表头
p->next=pthead->next;//插入新的位置
pthead->next=p;
break;
case 2:printf("pls modify sex:");scanf("%s",p->sex);//修改性别
break;
case 3:printf("pls modify age:");scanf("%d",&p->age);//修改年龄
break;
case 4:printf("pls modify id:");scanf("%d",&p->id); //修改id
break;
case 5:return 0;
}
}
}
//删除信息
int stu_info_delete(char*name)
{
struct stunode *p = stu_info_find_member(name);//查找信息
if(!p)
{
printf("sorry no found\n");
return -1;
}
int hashval=hash_calucate(p->name);//计算hash值
struct stunode *pre=&stu_head_arr[hashval];
while(pre->next!=p)//找到p前面一个地址pre
pre=pre->next;
pre->next=p->next;//p前面与p后面相连
}
int main(void)
{
char name[16];
while(1){
printf("*****************菜单*****************\n");
printf("1: add member\n");
printf("2: find member by name\n");
printf("3: modify member by name\n");
printf("4: delete member by name\n");
printf("5: exit\n");
printf("*****************菜单*****************\n");
int select;
scanf("%d",&select);
switch(select){
case 1:
stu_info_add_member();
break;
case 2:
printf("pls input your name:");
scanf("%s",name);
struct stunode *p = stu_info_find_member(name);
if(p)
{
printf("name:%s sex:%s age:%d id:%d \n",\
p->name,p->sex,p->age,p->id);
//如果有重名的,都打印出来
while((p->next!=NULL)&&(strcmp(p->name,p->next->name)==0))
{
printf("name:%s sex:%s age:%d id:%d \n",\
p->next->name,p->next->sex,p->next->age,p->next->id);
p=p->next;
}
}
else
printf("sorry no found\n");
break;
case 3:
printf("pls input your name:");
scanf("%s",name);
stu_info_change(name);
break;
case 4:
printf("pls input your name:");
scanf("%s",name);
stu_info_delete(name);
break;
case 5: return 0;
}
}
}
结果如下:
输入信息和查找信息
可以看见输入姓名、性别、年龄 ID之后能够查找到信息
![](https://img-blog.csdnimg.cn/img_convert/deffea3b173c1a00482d6223bb50329c.png)
修改信息
a.修改名字
![](https://img-blog.csdnimg.cn/img_convert/7b2d0af2dfe6748d3250c5db58a70be6.png)
b.修改年龄
![](https://img-blog.csdnimg.cn/img_convert/eb52a2b41e77e02c8ea497324682b3f1.png)
c.查看修改后的信息
可以看见修改成功了,也能查找到
![](https://img-blog.csdnimg.cn/img_convert/74bd375914335a15ee6b8c9bfeafeb62.png)
删除信息
可以看见删除“kang”之后再查找,显示“sorry no found”,说明删除成功了
![](https://img-blog.csdnimg.cn/img_convert/4cf0a040719324f2c2bd44c31055a155.png)