注:为避免混乱文章采取:
1: .c文件函数引用
2:.h文件函数定义
3:运行结果展示
的排版顺序,并且将.c和.h的完整代码放在文末
关于链表的五大功能的实现,本篇文章优势在于对输入输出边界的定义相对规范,以及功能编写的模块化,还有详解
如果感觉理解以下代码较为吃力,可以先行阅读有关线性表实现五大基础功能的相关案例,内附详细解答 http://t.csdnimg.cn/wDvDUhttp://t.csdnimg.cn/wDvDU
<1>单链表组成:
<2>查找元素:
<3> 链表元素插入:
<4>链表删除
一般会使用free()函数释放删除后的结点所占用的空间
<1>.c文件中.h文件的引用
为尽可能的简化主函数,其他的.h文件在自定义的"Module.h"中引用(关于#pragma once的应用)
#include"Module.h"
模拟操作面板的相关定义
int main()
{
int m = 0;
int flag = 0,elect=0;//flag函数执行成功的标志量,elect功能选择传值变量
printf("*-----------------------------------------------------------------------------------------*\n");
struct STU* List = CreaterList();//创建链表
traversal_List(List);//初始化链表
printf("*-----------------------------------------------------------------------------------------*\n\n");
while (1)
{
printf("**----------------------------------------------**\n");
printf("请选择您想选择的操作:\n");
printf("1:取值\t 2:查找\t3:插入\t4:删除\t5:退出\t\n");
scanf_s("%d", &elect);
switch (elect)
{
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
break;
default:
break;
printf("**----------------------------------------------**\n");
}
}
}
#pragma once
#include
#include
#include
#include
typedef struct STU
{
int data;//数据域
struct STU* next;//指针域,next是指针,指向结构体STU
};
注:<0.1><0.2>两个函数不属于操作功能函数,是为了避免相同操作重复编写,造成代码冗余而编写作为模块在其他函数中调用的,为避免混乱提前给出
因为我最后编写的插入功能模块,链表元素数目计算函数也是在那个时候加的,所以其他几个函数有用到这个功能的,都是直接编写没有调用Counter_List函数,有需要的读者可把相关代码删除后加上该函数,结果是一样的
//链表打印函数
int traversal_List(struct STU* head)
{
printf("列表信息为:");
for (struct STU* p = head->next; p != NULL; p=p->next)//链表循环
{
printf("%d\t",p->data);
}
printf("\n");
}
//链表元素个数计算
int Counter_List(struct STU* List)
{
int count0 = 0;
struct STU* p = List;
if (p->next == NULL)
{
printf("ERROR!!!空表");
return 0;
}
for (p = List; p != NULL; p = p->next)//链表循环计算链表元素个数
{
count0 += 1;
}
return count0-1;//count0从最后一个结点到NULL多计一次,比元素值多1
}
printf("*-----------------------------------------------------------------------------------------*\n");
struct STU* List = CreaterList();
traversal_List(List);
printf("*-----------------------------------------------------------------------------------------*\n\n");
//使用尾插法创建一个链表
struct STU* CreaterList()
{
//创建头结点
struct STU* head = (struct STU*)malloc(sizeof(struct STU));//将分配到的“struct STU”大小的内存转换为struct STU*类型
head->next = NULL;
struct STU* p = head;//struct STU类型的指针P指向头指针head
printf("请输入你想创建的链表元素个数:\n");
int amount = 0;
scanf_s("%d", &amount);
for (int i = 0; idata);//将值存放进knot结点中
//P指针指向链表新结点knot
p->next = knot;
//移动指针P
p = p->next;
knot->next = NULL;//确保链表最后一个一个结点的指针域指向NULL,否则它会指向一个不定的值
p->next = NULL;//确保p->next始终指向链表最后一个结点
}
return head;//返回头结点
}
case 1:
do {
flag = Get_Elem(List);//将List作为参数传进函数
} while (flag != 1);//flag为函数执行成功与否的标志量:1~成功执行;0:未成功执行
break;
//链表取值函数
int Get_Elem(struct STU* List)
{
int Choice = 0,count1=0,count2=0;
printf("请选择你想查询的序号:\n");
scanf_s("%d", &Choice,sizeof(Choice));
struct STU* p = List;
for (p = List; p != NULL; p = p->next)//链表循环计算链表元素个数
{
count1 += 1;
}//此时p指向NULL
p = List;//让p重新指向头结点
if ((Choice >= 1) && (Choice <= count1-1))//头结点也计了一次比链表元素个数多以
{
for (p = List->next; p != NULL; p = p->next)//链表循环
{
count2 += 1;//计数器先加1
if (count2 == Choice)
{
printf("所查询序号值为:%d\n", p->data);
}
}
return 1;
}
else
{
printf("序号超出范围,请重新输入:\n");
return 0;
}
}
该结果中同时包含一次,操作选择的非法输入(输入7>5)的运行结果展示,以及输入序号超出范围的运行结果展示,以及成功查询结果展示
case 2:
do {
flag = Find_Elem(List);
} while (flag != 1);
break;
//链表元素查找
int Find_Elem(struct STU* List)
{
int Element = 0, count3 = 0,count4=0,flag0=0;
printf("请选择你想查询的数值:\n");
scanf_s("%d", &Element, sizeof(Element));
struct STU* p = List;
for (p = List; p != NULL; p = p->next)//链表循环计算链表元素个数
{
count4 += 1;
}
for (p = List; p != NULL; p = p->next)//链表循环
{
count3 += 1;//计数器先加1
if (Element == p->next->data)
{
flag0 = 1;//当检索到该值时标志量等于1
printf("该元素位于链表的第%d位\n", count3);//因为count4在头结点也计数一次所以其值比元素个数多1
return 1;
}
if ((count3 == count4-1) && (flag0 == 0))//count4在链表的头结点也计数一次,所以比count3大1
{
printf("未在链表中检索到该元素,请重新输入:\n");
return 0;
}
}
}
该结果中同时包含一次,操作选择的非法输入(输入6>5)的运行结果展示,以及输入数据不在链表元素内的运行结果展示,以及成功查询结果展示
case 3:
do {
flag = Insert_List(List);
} while (flag != 1);
if (flag == 1)
traversal_List(List);//遍历链表
break;
//链表插入
int Insert_List(struct STU* List)
{
int position = 0,count5=0,count6=0,flag3 = 0;
struct STU* p = List;
printf("新元素将插在第几号元素后面:\n");
scanf_s("%d", &position, sizeof(position));
count5 = Counter_List(List);
if((position>count5)||(position<0))
{
printf("位置非法,请重新输入\n");
return 0;
}
else
{
while (count6 != position)
{
p = p->next;
count6 += 1;
}
if (count6 == count5)
{
//创建新结点
struct STU* Newknot = (struct STU*)malloc(sizeof(struct STU));
//存储数据
printf("请输入插入元素的数据:\n");
scanf_s("%d", &Newknot->data);//将值存放进knot结点中
//P指针指向链表新结点knot
p->next = Newknot;
//移动指针P
p = p->next;
Newknot->next = NULL;//确保链表最后一个一个结点的指针域指向NULL,否则它会指向一个不定的值
p->next = NULL;//确保p->next始终指向链表最后一个结点
return 1;
}
else//注意二者区别,前者为尾插,后者为在链表中间插入
{
//创建新结点
struct STU* Newknot = (struct STU*)malloc(sizeof(struct STU));
//存储数据
printf("请输入插入元素的数据:\n");
scanf_s("%d", &Newknot->data);//将值存放进knot结点中
Newknot->next = p->next;
p->next = Newknot;
return 1;
}
}
}
该结果中同时包含一次,操作选择的非法输入(输入6>5)的运行结果展示,以及插入位置前、中间、后一位,地方(非法插入)的运行结果展示,以及插入成功的结果展示
case 4:
do {
flag = Delete_List(List);
} while (flag != 1);
if(flag==1)
traversal_List(List);//遍历链表
break;
删除函数设计了两种删除模式:按值删除 和 按序号删除
//删除链表元素
int Delete_List(struct STU* List)
{
if (List == NULL)
{
return 0;
}
int select = 0;
printf("请选择删除方式:1:按值删除 2:按序号删除 3:退出\n");
scanf_s("%d",&select);
struct STU* p=List;
switch (select)
{
case 1:
printf("请输入你想删除的数据:\n");
int datas=0,flag1=0;
scanf_s("%d", &datas,sizeof(datas));
for (p = List; p!= NULL; p=p->next)//链表循环
{
if (p->next->data == datas) //如果输入值与链表中某结点值相等
{ //此时p指向了含有data值的上一个结点,
flag1 = 1;//当在链表中找到需要删除的数据,标志位为1
struct STU* delect = p->next;//使用临时指针指向待删除的结点
p->next = delect->next;//p->next指向待删除结点的下一个结点
free(delect);//释放删除结点
return 1;
break;
}
if ((p->next->next == NULL) && (p->next->data != datas) && (flag1 == 0))//注意我们在创建数组时定义了最后一个元素的指针域为NULL
{
printf("未检索到该数值,请重新输入:\n");
return 0;
}
}
break;
case 2:
printf("请输入你想删除的序号:\n");//按序号删除和按值删除原理是一样的
int number=0,counter=0;
int flag2 = 0;
scanf_s("%d", &number,sizeof(number));
for (p = List; p!= NULL; p = p->next)//链表循环
{
counter += 1;
if (counter == number)
{
flag2 = 1;//当在链表中找到需要删除的数据,标志位为1
struct STU* delect = p->next;//使用临时指针指向待删除的结点
p->next = delect->next;//p->next指向待删除结点的下一个结点
free(delect);//释放删除结点
return 1;
}
else if ((p->next->next == NULL)&&(counter != number) && (flag2 == 0))
{
printf("输入序号超出范围,请重新操作:\n");
return 0;
}
}
break;
case 3:exit(0);
break;
default:
printf("非法输入,请重新输入:\n");
return 0;
break;
}
}
结果展示1:该结果中同时包含一次,操作选择的非法输入(输入17>5)的运行结果展示,以及子操作目录选择功能时输入4超出选择范围(1-3)的运行结果展示,以及先按值删除后按序号删除的运行结果
结果展示2:该运行结果展示了按值删除时未输入链表内的值 按序号查找时未输入合法序号 两种情况的运行结果
case 5:
exit(0);
break;
我也是无语了,把代码放上来的时候才发现default中的printf(“非法输入请重新操作”);没写上去,所以导致我们之前的重新操作的时候没有提示信息直接让重新输入值;
因为重新截图好麻烦,我就不加了,大家知道一下就好
default:
printf("非法输入请重新操作");
break;
#include"Module.h"
int main()
{
int m = 0;
int flag = 0,elect=0;
printf("*-----------------------------------------------------------------------------------------*\n");
struct STU* List = CreaterList();
traversal_List(List);
printf("*-----------------------------------------------------------------------------------------*\n\n");
while (1)
{
printf("**----------------------------------------------**\n");
printf("请选择您想选择的操作:\n");
printf("1:取值\t 2:查找\t3:插入\t4:删除\t5:退出\t\n");
scanf_s("%d", &elect);
switch (elect)
{
case 1:
do {
flag = Get_Elem(List);
} while (flag != 1);
break;
case 2:
do {
flag = Find_Elem(List);
} while (flag != 1);
break;
case 3:
do {
flag = Insert_List(List);
} while (flag != 1);
if (flag == 1)
traversal_List(List);//遍历链表
break;
case 4:
do {
flag = Delete_List(List);
} while (flag != 1);
if(flag==1)
traversal_List(List);//遍历链表
break;
case 5:
exit(0);
break;
default:
printf("非法输入请重新操作");
break;
printf("**----------------------------------------------**\n");
}
}
}
#pragma once
#include
#include
#include
#include
typedef struct STU
{
int data;//数据域
struct STU* next;//指针域,next是指针,指向结构体STU
};
//使用尾插法创建一个链表
struct STU* CreaterList()
{
//创建头结点
struct STU* head = (struct STU*)malloc(sizeof(struct STU));//将分配到的“struct STU”大小的内存转换为struct STU*类型
head->next = NULL;
struct STU* p = head;//struct STU类型的指针P指向头指针head
printf("请输入你想创建的链表元素个数:\n");
int amount = 0;
scanf_s("%d", &amount);
for (int i = 0; idata);//将值存放进knot结点中
//P指针指向链表新结点knot
p->next = knot;
//移动指针P
p = p->next;
knot->next = NULL;//确保链表最后一个一个结点的指针域指向NULL,否则它会指向一个不定的值
p->next = NULL;//确保p->next始终指向链表最后一个结点
}
return head;//返回头结点
}
//链表打印函数
int traversal_List(struct STU* head)
{
printf("列表信息为:");
for (struct STU* p = head->next; p != NULL; p=p->next)//链表循环
{
printf("%d\t",p->data);
}
printf("\n");
}
//链表元素个数计算
int Counter_List(struct STU* List)
{
int count0 = 0;
struct STU* p = List;
if (p->next == NULL)
{
printf("ERROR!!!空表");
return 0;
}
for (p = List; p != NULL; p = p->next)//链表循环计算链表元素个数
{
count0 += 1;
}
return count0-1;//count0从最后一个结点到NULL多计一次,比元素值多1
}
//链表取值函数
int Get_Elem(struct STU* List)
{
int Choice = 0,count1=0,count2=0;
printf("请选择你想查询的序号:\n");
scanf_s("%d", &Choice,sizeof(Choice));
struct STU* p = List;
for (p = List; p != NULL; p = p->next)//链表循环计算链表元素个数
{
count1 += 1;
}//此时p指向NULL
p = List;//让p重新指向头结点
if ((Choice >= 1) && (Choice <= count1-1))//头结点也计了一次比链表元素个数多以
{
for (p = List->next; p != NULL; p = p->next)//链表循环
{
count2 += 1;//计数器先加1
if (count2 == Choice)
{
printf("所查询序号值为:%d\n", p->data);
}
}
return 1;
}
else
{
printf("序号超出范围,请重新输入:\n");
return 0;
}
}
//链表元素查找
int Find_Elem(struct STU* List)
{
int Element = 0, count3 = 0,count4=0,flag0=0;
printf("请选择你想查询的数值:\n");
scanf_s("%d", &Element, sizeof(Element));
struct STU* p = List;
for (p = List; p != NULL; p = p->next)//链表循环计算链表元素个数
{
count4 += 1;
}
for (p = List; p != NULL; p = p->next)//链表循环
{
count3 += 1;//计数器先加1
if (Element == p->next->data)
{
flag0 = 1;//当检索到该值时标志量等于1
printf("该元素位于链表的第%d位\n", count3);//因为count4在头结点也计数一次所以其值比元素个数多1
return 1;
}
if ((count3 == count4-1) && (flag0 == 0))//count4在链表的头结点也计数一次,所以比count3大1
{
printf("未在链表中检索到该元素,请重新输入:\n");
return 0;
}
}
}
//删除链表元素
int Delete_List(struct STU* List)
{
if (List == NULL)
{
return 0;
}
int select = 0;
printf("请选择删除方式:1:按值删除 2:按序号删除 3:退出\n");
scanf_s("%d",&select);
struct STU* p=List;
switch (select)
{
case 1:
printf("请输入你想删除的数据:\n");
int datas=0,flag1=0;
scanf_s("%d", &datas,sizeof(datas));
for (p = List; p!= NULL; p=p->next)//链表循环
{
if (p->next->data == datas) //如果输入值与链表中某结点值相等
{ //此时p指向了含有data值的上一个结点,
flag1 = 1;//当在链表中找到需要删除的数据,标志位为1
struct STU* delect = p->next;//使用临时指针指向待删除的结点
p->next = delect->next;//p->next指向待删除结点的下一个结点
free(delect);//释放删除结点
return 1;
break;
}
if ((p->next->next == NULL) && (p->next->data != datas) && (flag1 == 0))//注意我们在创建数组时定义了最后一个元素的指针域为NULL
{
printf("未检索到该数值,请重新输入:\n");
return 0;
}
}
break;
case 2:
printf("请输入你想删除的序号:\n");//按序号删除和按值删除原理是一样的
int number=0,counter=0;
int flag2 = 0;
scanf_s("%d", &number,sizeof(number));
for (p = List; p!= NULL; p = p->next)//链表循环
{
counter += 1;
if (counter == number)
{
flag2 = 1;//当在链表中找到需要删除的数据,标志位为1
struct STU* delect = p->next;//使用临时指针指向待删除的结点
p->next = delect->next;//p->next指向待删除结点的下一个结点
free(delect);//释放删除结点
return 1;
}
else if ((p->next->next == NULL)&&(counter != number) && (flag2 == 0))
{
printf("输入序号超出范围,请重新操作:\n");
return 0;
}
}
break;
case 3:exit(0);
break;
default:
printf("非法输入,请重新输入:\n");
return 0;
break;
}
}
//链表插入
int Insert_List(struct STU* List)
{
int position = 0,count5=0,count6=0,flag3 = 0;
struct STU* p = List;
printf("新元素将插在第几号元素后面:\n");
scanf_s("%d", &position, sizeof(position));
count5 = Counter_List(List);
if((position>count5)||(position<0))
{
printf("位置非法,请重新输入\n");
return 0;
}
else
{
while (count6 != position)
{
p = p->next;
count6 += 1;
}
if (count6 == count5)
{
//创建新结点
struct STU* Newknot = (struct STU*)malloc(sizeof(struct STU));
//存储数据
printf("请输入插入元素的数据:\n");
scanf_s("%d", &Newknot->data);//将值存放进knot结点中
//P指针指向链表新结点knot
p->next = Newknot;
//移动指针P
p = p->next;
Newknot->next = NULL;//确保链表最后一个一个结点的指针域指向NULL,否则它会指向一个不定的值
p->next = NULL;//确保p->next始终指向链表最后一个结点
return 1;
}
else//注意二者区别,前者为尾插,后者为在链表中间插入
{
//创建新结点
struct STU* Newknot = (struct STU*)malloc(sizeof(struct STU));
//存储数据
printf("请输入插入元素的数据:\n");
scanf_s("%d", &Newknot->data);//将值存放进knot结点中
Newknot->next = p->next;
p->next = Newknot;
return 1;
}
}
}