单链表实现学生管理系统
程序的结构体数据类型使用int型替代学生结构体,其他内容都ok
1.创建单链表
PLink create(){//创建头节点
PLink P=(PLink)malloc(sizeof(mylink));
if(NULL==P){
perror("create error");
return NULL;
}
P->len=0;
P->next=NULL;
return P;//返回头节点地址
}
2.头插法
int front_insert(PLink L,int a){//头部插入元素
if(NULL==L){
printf("单链表不存在,创建失败\n");
return -1;
}
PLink P=(PLink)malloc(sizeof(mylink));//申请新节点
P->data=a;//数据域赋值
P->next=L->next;//连线右边
L->next=P;//连线左边//顺序不能颠倒
L->len++;
return 0;
}
3.尾插法
int rear_insert(PLink L,int a){//尾部插入元素
if(NULL==L){
printf("单链表不存在,创建失败\n");
return -1;
}
PLink t=L;
for(int i=0;i<L->len;i++){//遍历单链表,找到尾,因为尾插是在最后节点后面即len+1位置插入
//,所以我们遍历len次,不是len-1次,找到len+1节点的前面节点
t=t->next;
}
PLink P=(PLink)malloc(sizeof(mylink));//申请新节点
P->data=a;//数据域赋值
t->next=P;
P->next=NULL;
L->len++;
return 0;
}
4.遍历单链表
int output_link(PLink L){//遍历单链表
int i;
PLink t=L;
for(i=0;i<L->len;i++){//遍历len次
t=t->next;
printf("%d\t",t->data);//打印数据域
}
printf("\n");
return 0;
}
5.任意位置插入元素
int anypos_insert(PLink L,int post,int key){
if(NULL==L||post<1||post>L->len+1){
printf("任意位置插入失败\n");
return -1;
}
int i;
PLink t=L;
for(i=1;i<post;i++){//移动post-1次指向待插入节点的前驱
t=t->next;
}
PLink P=(PLink)malloc(sizeof(mylink));//申请新节点
P->data=key;//数据域赋值
P->next=t->next;//指向右边
t->next=P;//指向左边//顺序不能颠倒
L->len++;//长度+1
return 0;
}
6.任意位置删除元素
int anypos_delete(PLink L,int post){
if(NULL==L||post<1||post>L->len||L->len==0){
printf("任意位置删除失败\n");
return -1;
}
int i;
PLink t=L;
for(i=1;i<post;i++){//移动post-1次指向待删除节点的前驱
t=t->next;
}
PLink Q=t->next;//保留要删除的节点地址
t->next=t->next->next;//跨过要删除节点
L->len--;//长度-1
free(Q);//释放要删除的空间
Q=NULL;//指针置空
return 0;
}
7.任意位置查找元素
int anypos_find(PLink L,int post){
if(NULL==L||post<1||post>L->len){
printf("任意位置查找失败\n");
return -1;
}
int i;
PLink t=L;
for(i=0;i<post;i++){//移动post次后,t指向post节点
t=t->next;
}
printf("找到了,该节点数据为:%d\n",t->data);
return 0;
}
8.任意位置修改元素
int anypos_change(PLink L,int post,int key){
if(NULL==L||post<1||post>L->len){
printf("任意位置修改失败\n");
return -1;
}
int i;
PLink t=L;
for(i=0;i<post;i++){//移动post次后,t指向post节点
t=t->next;
}
t->data=key;//修改post位置节点数据内容
printf("修改成功\n");
return 0;
}
9.头删法
int front_delete(PLink L){
if(NULL==L||L->len==0){
printf("头删失败\n");
return -1;
}
PLink Q=L->next;//保留要删除的节点地址
L->next=L->next->next;//跨国要删除的节点
L->len--;//长度-1
free(Q);//释放要删除节点空间
Q=NULL;//释放后指向地址置空
return 0;
}
10.尾删法
int rear_delete(PLink L){
if(NULL==L||L->len==0){
printf("尾删失败\n");
return -1;
}
PLink t=L;
int i;
for(i=1;i<L->len;i++){//执行len-1次找到尾节点的前一个节点
t=t->next;
}
PLink Q=t->next;//保留尾节点的地址
t->next=NULL;//尾节点删除
L->len--;//长度-1
free(Q);//释放尾节点空间
Q=NULL;//指针置空
return 0;
}
11.按值删除所有相同元素
int value_all_delete(PLink L,int value){
if(NULL==L||L->len==0){
printf("按值删除失败\n");
return -1;
}
PLink t=L;
int i,sub=-1;
for(i=0;i<L->len;i++){
if(t->next->data==value){//按值删除所有相同的值
PLink Q=t->next;//存储要删除节点地址
t->next=t->next->next;//跨过要删除节点
L->len--;//长度-1
free(Q);//释放删除节点空间
Q=NULL;//指针置空
sub=0;
i--;
continue;//删除一个节点后,不让t指向下一个节点,避免前后节点数据值相同,t移动导致漏删!!!
}
t=t->next;//遍历链表
}
if(sub==-1){
printf("链表中没有相同的值,删除失败\n");
return -1;
}
return 0;
}
12.按值删除最后相同元素
int value_last_delete(PLink L,int value){
if(NULL==L||L->len==0){
printf("按值删除失败\n");
return -1;
}
PLink t=L,Q;
int i,sub=-1;
for(i=0;i<L->len;i++){
if(t->next->data==value){
Q=t;//保留要删除最后节点前一个节点地址
sub=0;
}
t=t->next;//遍历链表
}
if(sub==0){
PLink W=Q->next;//保留要删除节点的地址
Q->next=Q->next->next;//跨过要删除节点
L->len--;//长度-1
free(W);//释放要删除节点空间
W=NULL;//指针置空
return 0;
}else{
printf("链表中没有相同的值,删除失败\n");
return -1;
}
}
13.按值修改最后相同元素
int value_last_change(PLink L,int key,int value){
if(NULL==L){
printf("按值修改失败\n");
return -1;
}
PLink t=L,Q;
int i,sub=-1;
for(i=0;i<L->len;i++){
t=t->next;//遍历链表
if(t->data==key){
Q=t;//保留要修改最后节点的地址
sub=0;
}
}
if(sub==0){
Q->data=value;
printf("修改成功\n");
return 0;
}else{
printf("链表中没有相同的值,修改失败\n");
return -1;
}
}
14.按值查找最后相同元素并输出地址
int value_all_find(PLink L,int value){
if(NULL==L){
printf("按值查找失败\n");
return -1;
}
PLink t=L;
int i,sub=-1;
for(i=0;i<L->len;i++){
t=t->next;//遍历链表
if(t->data==value){
printf("找到了,地址为%p\n",t);
sub=0;
}
}
if(sub==-1){
printf("链表中没有找到相同的值,查找失败\n");
}
return 0;
}
15.逆序单链表
int reverse_link(PLink L){
if(NULL==L){
printf("逆序失败\n");
return -1;
}
/* 第一种逆序方式
PLink temp=L->next;
PLink next_node=L->next;//保留下一个节点的地址,避免丢失
while(temp!=NULL){//temp与next_node错开一个节点,temp进行头插操作,实现逆序
next_node=next_node->next;
temp->next=L->next;
L->next=temp;
temp=next_node;
}
temp=L->next;//若开辟一个新的头节点且next初始化为NULL,则不需要下面操作
for(int i=0;i<L->len;i++){
temp=temp->next;
}
temp->next=NULL;
*/
//第二种逆序方式
PLink Q,t;
Q=L->next;//指向第一个节点
t=Q->next;//指向第二个节点
while(t!=NULL){
Q->next=t->next;//将1号和3号节点链接上
t->next=L->next;//头插操作
L->next=t;
t=Q->next;//进行下一轮节点插入
}
}
16.链表去重
int repeat_link(PLink L){
if(empty(L)){
printf("链表去重失败\n");
return-1;
}
/*第一种方式,i和j都是int,不是指针
PLink Q=L,W;
for(int i=0;i<L->len;i++){
Q=Q->next;
W=Q;//一轮循环下来,W应重新赋值指向Q的节点
for(int j=i+1;j<L->len;j++){
if(Q->data==W->next->data){
PLink Z=W->next;
W->next=W->next->next;
L->len--;
free(Z);
Z=NULL;
j--;//多执行一次避免漏删
continue;//多个重复项连续存在时,避免漏删
}
W=W->next;
}
}*/
//第二种方式,i和j都是指针,比第一种优化一些
PLink i,j;
for(i=L->next;i->next!=NULL;i=i->next){
for(j=i;j->next!=NULL;){
if(i->data==j->next->data){//注意删除是要j指向前一节点地址
PLink t=j->next;//保留要删除节点的地址
j->next=j->next->next;//跨过要删除节点
L->len--;
free(t);
t=NULL;
}else{
j=j->next;//当出现重复项时,j的地址位置不要移动,因为j->next已经更新,此时移动j,下次比较时,
//j->next->data会跨过一个未比较的数据,避免多个重复项连续时,出现漏删
}
}
}
}
17.排序-冒泡排序
int bubble_sort_Link(PLink L){
if(empty(L)){
printf("冒泡排序失败\n");
return -1;
}
int i,j,temp;
PLink t;//用于遍历链表
for(i=1;i<L->len;i++){
t=L->next;//遍历一遍后,t指针重新赋值用于再次遍历
for(j=0;j<L->len-i;j++){
if(t->data>t->next->data){
temp=t->data;
t->data=t->next->data;
t->next->data=temp;
}
t=t->next;//遍历链表
}
}
}
18.链表空间释放
int free_link(PLink L){
if(NULL==L){
printf("链表创建失败\n");
return -1;
}
/*int i;//销毁时不能用len做判断条件,因为L会释放掉,应该采用判空
for(i=0;i<L->len+1;i++){//采用循环方式依次释放malloc开辟的动态空间,并指针依次置空
PLink Q=L->next;
free(L);
L=NULL;
L=Q;
}*/
PLink t=L;
while(t!=NULL){
t=t->next;
free(L);
L==NULL;
L=t;
}
printf("销毁成功\n");
return 0;
}
分文件代码
main.c
#include "link.h"
int main(int argc, const char *argv[])
{
int a[10]={1,2,3,4,7,7,8,8,8,10};
//1.创建单链表
PLink L=create();//创建头节点
#if 0
//1.1头插法
int i;
for(i=0;i<10;i++){
front_insert(L,a[i]);
}
#endif
//1.2尾插法
for(int i=0;i<10;i++){
rear_insert(L,a[i]);
}
#if 0
//2.遍历单链表
output_link(L);
//3.任意位置插入一个节点
anypos_insert(L,4,15);
output_link(L);
//4.任意位置删除一个节点
anypos_delete(L,2);
output_link(L);
//5.任意位置查找一个节点
anypos_find(L,8);
//6.任意位置修改一个节点
anypos_change(L,7,999);
output_link(L);
//7.头删
front_delete(L);
output_link(L);
//8.尾删
rear_delete(L);
output_link(L);
//9.按值删除(删除所有相同的值)
value_all_delete(L,7);
output_link(L);
//10.按值删除(删除最后相同的值)
value_last_delete(L,8);
output_link(L);
//11.按值修改(修改最后相同的值)
value_last_change(L,7,888);
output_link(L);
//12.按值查找返回地址(打印所有值相同的地址)
value_all_find(L,8);
#endif
//13.链表逆置
reverse_link(L);
output_link(L);
//14.链表去重
repeat_link(L);
output_link(L);
//15.链表排序(冒泡、插值排序-创建一个新链表去实现)
bubble_sort_Link(L);
output_link(L);
//16.销毁
free_link(L);
L=NULL;//指针置空
return 0;
}
link.c
#include "link.h"
PLink create(){//创建头节点
PLink P=(PLink)malloc(sizeof(mylink));
if(NULL==P){
perror("create error");
return NULL;
}
P->len=0;
P->next=NULL;
return P;//返回头节点地址
}
int front_insert(PLink L,int a){//头部插入元素
if(NULL==L){
printf("单链表不存在,创建失败\n");
return -1;
}
PLink P=(PLink)malloc(sizeof(mylink));//申请新节点
P->data=a;//数据域赋值
P->next=L->next;//连线右边
L->next=P;//连线左边//顺序不能颠倒
L->len++;
return 0;
}
int rear_insert(PLink L,int a){//尾部插入元素
if(NULL==L){
printf("单链表不存在,创建失败\n");
return -1;
}
PLink t=L;
for(int i=0;i<L->len;i++){//遍历单链表,找到尾,因为尾插是在最后节点后面即len+1位置插入
//,所以我们遍历len次,不是len-1次,找到len+1节点的前面节点
t=t->next;
}
PLink P=(PLink)malloc(sizeof(mylink));//申请新节点
P->data=a;//数据域赋值
t->next=P;
P->next=NULL;
L->len++;
return 0;
}
int output_link(PLink L){//遍历单链表
int i;
PLink t=L;
for(i=0;i<L->len;i++){//遍历len次
t=t->next;
printf("%d\t",t->data);//打印数据域
}
printf("\n");
return 0;
}
int anypos_insert(PLink L,int post,int key){
if(NULL==L||post<1||post>L->len+1){
printf("任意位置插入失败\n");
return -1;
}
int i;
PLink t=L;
for(i=1;i<post;i++){//移动post-1次指向待插入节点的前驱
t=t->next;
}
PLink P=(PLink)malloc(sizeof(mylink));//申请新节点
P->data=key;//数据域赋值
P->next=t->next;//指向右边
t->next=P;//指向左边//顺序不能颠倒
L->len++;//长度+1
return 0;
}
int anypos_delete(PLink L,int post){
if(NULL==L||post<1||post>L->len||L->len==0){
printf("任意位置删除失败\n");
return -1;
}
int i;
PLink t=L;
for(i=1;i<post;i++){//移动post-1次指向待删除节点的前驱
t=t->next;
}
PLink Q=t->next;//保留要删除的节点地址
t->next=t->next->next;//跨过要删除节点
L->len--;//长度-1
free(Q);//释放要删除的空间
Q=NULL;//指针置空
return 0;
}
int anypos_find(PLink L,int post){
if(NULL==L||post<1||post>L->len){
printf("任意位置查找失败\n");
return -1;
}
int i;
PLink t=L;
for(i=0;i<post;i++){//移动post次后,t指向post节点
t=t->next;
}
printf("找到了,该节点数据为:%d\n",t->data);
return 0;
}
int anypos_change(PLink L,int post,int key){
if(NULL==L||post<1||post>L->len){
printf("任意位置修改失败\n");
return -1;
}
int i;
PLink t=L;
for(i=0;i<post;i++){//移动post次后,t指向post节点
t=t->next;
}
t->data=key;//修改post位置节点数据内容
printf("修改成功\n");
return 0;
}
int front_delete(PLink L){
if(NULL==L||L->len==0){
printf("头删失败\n");
return -1;
}
PLink Q=L->next;//保留要删除的节点地址
L->next=L->next->next;//跨国要删除的节点
L->len--;//长度-1
free(Q);//释放要删除节点空间
Q=NULL;//释放后指向地址置空
return 0;
}
int rear_delete(PLink L){
if(NULL==L||L->len==0){
printf("尾删失败\n");
return -1;
}
PLink t=L;
int i;
for(i=1;i<L->len;i++){//执行len-1次找到尾节点的前一个节点
t=t->next;
}
PLink Q=t->next;//保留尾节点的地址
t->next=NULL;//尾节点删除
L->len--;//长度-1
free(Q);//释放尾节点空间
Q=NULL;//指针置空
return 0;
}
int value_all_delete(PLink L,int value){
if(NULL==L||L->len==0){
printf("按值删除失败\n");
return -1;
}
PLink t=L;
int i,sub=-1;
for(i=0;i<L->len;i++){
if(t->next->data==value){//按值删除所有相同的值
PLink Q=t->next;//存储要删除节点地址
t->next=t->next->next;//跨过要删除节点
L->len--;//长度-1
free(Q);//释放删除节点空间
Q=NULL;//指针置空
sub=0;
i--;//多执行一次,避免漏删
continue;//删除一个节点后,不让t指向下一个节点,避免前后节点数据值相同,t移动导致漏删!!!
}
t=t->next;//遍历链表
}
if(sub==-1){
printf("链表中没有相同的值,删除失败\n");
return -1;
}
return 0;
}
int empty(PLink L){
if(NULL==L||L->len==0){
return 1;
}
return 0;
}
int value_last_delete(PLink L,int value){
if(empty(L)){
printf("按值删除失败\n");
return -1;
}
PLink t=L,Q;
int i,sub=-1;
for(i=0;i<L->len;i++){
if(t->next->data==value){
Q=t;//保留要删除最后节点前一个节点地址
sub=0;
}
t=t->next;//遍历链表
}
if(sub==0){
PLink W=Q->next;//保留要删除节点的地址
Q->next=Q->next->next;//跨过要删除节点
L->len--;//长度-1
free(W);//释放要删除节点空间
W=NULL;//指针置空
return 0;
}else{
printf("链表中没有相同的值,删除失败\n");
return -1;
}
}
int value_last_change(PLink L,int key,int value){
if(NULL==L){
printf("按值修改失败\n");
return -1;
}
PLink t=L,Q;
int i,sub=-1;
for(i=0;i<L->len;i++){
t=t->next;//遍历链表
if(t->data==key){
Q=t;//保留要修改最后节点的地址
sub=0;
}
}
if(sub==0){
Q->data=value;
printf("修改成功\n");
return 0;
}else{
printf("链表中没有相同的值,修改失败\n");
return -1;
}
}
int value_all_find(PLink L,int value){
if(NULL==L){
printf("按值查找失败\n");
return -1;
}
PLink t=L;
int i,sub=-1;
for(i=0;i<L->len;i++){
t=t->next;//遍历链表
if(t->data==value){
printf("找到了,地址为%p\n",t);
sub=0;
}
}
if(sub==-1){
printf("链表中没有找到相同的值,查找失败\n");
}
return 0;
}
int reverse_link(PLink L){
if(NULL==L){
printf("逆序失败\n");
return -1;
}
/* 第一种逆序方式
PLink temp=L->next;
PLink next_node=L->next;//保留下一个节点的地址,避免丢失
while(temp!=NULL){//temp与next_node错开一个节点,temp进行头插操作,实现逆序
next_node=next_node->next;
temp->next=L->next;
L->next=temp;
temp=next_node;
}
temp=L->next;//若开辟一个新的头节点且next初始化为NULL,则不需要下面操作
for(int i=0;i<L->len;i++){
temp=temp->next;
}
temp->next=NULL;
*/
//第二种逆序方式
PLink Q,t;
Q=L->next;//指向第一个节点
t=Q->next;//指向第二个节点
while(t!=NULL){
Q->next=t->next;//将1号和3号节点链接上
t->next=L->next;//头插操作
L->next=t;
t=Q->next;//进行下一轮节点插入
}
}
int repeat_link(PLink L){
if(empty(L)){
printf("链表去重失败\n");
return-1;
}
/*第一种方式,i和j都是int,不是指针
PLink Q=L,W;
for(int i=0;i<L->len;i++){
Q=Q->next;
W=Q;//一轮循环下来,W应重新赋值指向Q的节点
for(int j=i+1;j<L->len;j++){
if(Q->data==W->next->data){
PLink Z=W->next;
W->next=W->next->next;
L->len--;
free(Z);
Z=NULL;
j--;//多执行一次避免漏删
continue;//多个重复项连续存在时,避免漏删
}
W=W->next;
}
}*/
//第二种方式,i和j都是指针,比第一种优化一些
PLink i,j;
for(i=L->next;i->next!=NULL;i=i->next){
for(j=i;j->next!=NULL;){
if(i->data==j->next->data){//注意删除是要j指向前一节点地址
PLink t=j->next;//保留要删除节点的地址
j->next=j->next->next;//跨过要删除节点
L->len--;
free(t);
t=NULL;
}else{
j=j->next;//当出现重复项时,j的地址位置不要移动,因为j->next已经更新,此时移动j,下次比较时,
//j->next->data会跨过一个未比较的数据,避免多个重复项连续时,出现漏删
}
}
}
}
int bubble_sort_Link(PLink L){
if(empty(L)){
printf("冒泡排序失败\n");
return -1;
}
int i,j,temp;
PLink t;//用于遍历链表
for(i=1;i<L->len;i++){
t=L->next;//遍历一遍后,t指针重新赋值用于再次遍历
for(j=0;j<L->len-i;j++){
if(t->data>t->next->data){
temp=t->data;
t->data=t->next->data;
t->next->data=temp;
}
t=t->next;//遍历链表
}
}
}
int free_link(PLink L){
if(NULL==L){
printf("链表创建失败\n");
return -1;
}
/*int i;//销毁时不能用len做判断条件,因为L会释放掉,应该采用判空
for(i=0;i<L->len+1;i++){//采用循环方式依次释放malloc开辟的动态空间,并指针依次置空
PLink Q=L->next;
free(L);
L=NULL;
L=Q;
}*/
PLink t=L;
while(t!=NULL){
t=t->next;
free(L);
L==NULL;
L=t;
}
printf("销毁成功\n");
return 0;
}
link.h
#ifndef __LINK__
#define __LINK__
#include <myhead.h>
typedef struct stu{
union{
int len;//用于头节点,统计节点个数
int data;//用于正常节点,存储数据
};
struct stu *next;//指针域
}mylink,*PLink;
//*******************
//函数申明
PLink create();
int front_insert(PLink,int);
int output_link(PLink L);
int rear_insert(PLink,int);
int insert(PLink,int,int);
int anypos_insert(PLink,int,int);
int anypos_delete(PLink,int);
int anypos_find(PLink,int);
int anypos_change(PLink,int,int);
int front_delete(PLink);
int rear_delete(PLink);
int empty(PLink);
int value_all_delete(PLink,int);
int value_last_delete(PLink,int);
int value_last_change(PLink,int,int);
int value_all_find(PLink,int);
int reverse_link(PLink);
int repeat_link(PLink);
int bubble_sort_Link(PLink);
int free_link(PLink);
#endif