单链表的使用
在数据结构中,根据元素之间的不同特性,通常有下列4类基本结构:集合、线性结构、树形结构、图状结构或网状结构。本次,笔者将会奉上豪华的链表套餐,希望新学者可以更快的掌握单链表的插入、删除操作。
线性结构中有两种存储结构,一种是顺序存储结构,一种是链式存储结构,本次,只会接受链式存储结构,之后会补更顺序存储结构。
顺序存储结构查找比较方便,但是插入和删除操作需要移动大量元素,因此插入和删除较为麻烦,而单链表却刚好与之相反,它插入和删除比较方便,但查找却麻烦。因此何时使用链式和顺序,需要根据自己的需求合理使用,从而达到效率最大化。
对于链表,之前我们介绍过了,它的插入和删除比较方便,因此在实际应用中,我们常常会使用到这两个操作,因为笔者不怎么会使用编辑器,不会画动态图,所以可能很难把这个问题给大家演示清楚,但是笔者还是会尽力在代码中多加注释,方便大家的理解。如果大家有课本的话,对照课本中的动态图,再结合笔者的注释和通俗易懂的代码,相信大家都能很快的掌握对于单链表的使用。加油
/*
1、程序设计规划(实现的功能、分几个模块、子函数)
2、 编写单链表创建子函数
3、 编写单链表插入某个结点的子函数 提示:在插入到第i个位置时,要先找到第i-1的结点,然后操作
4、 编写单链表删除某个结点的子函数 提示:在删除到第i个位置时,也要先找到第i-1的结点,然后操作
5、 编写单链表查询某个结点的子函数
6、 编写单链表显示子函数
7、 编写主函数Main(),通过功能菜单调用子函数
8、 编译调试程序
*/
//本程序中函数名的定义规则:多单次组合时第一个单次首字母小写,第二个单词开始每个单次首字母大写(采用了java中的方法的命名规则)
#include<stdio.h>
#include<stdlib.h>
typedef int Status; //在这里给int起别名Status
typedef int ElemType; //在这里给int起别名ElemType
//建立结构体,通过结构体建立链表指针、next域、data域
typedef struct LNode{
ElemType data; //data域,用来存放结点中的值
struct LNode *next; //用来指向下一个结点
}LNode,*LinkList;
//打印链表
void printList(LinkList &L)
{
LinkList p; //定义变量p
p=L->next; //将p变为第一个结点(头结点的下一个)
while(p!=NULL){ //当p不空时候循环
printf("%d\t",p->data); //输出结点p对应的值;
p=p->next; //令p指向p的下一个结点
}
printf("\n");
}
//建立一个创造链表并初始化的函数
void creatList(LinkList &L,Status number) //number为链表中结点的个数
{
L=(LinkList)malloc(sizeof(LNode)); //为L动态分配内存空间
if(L==NULL){ //增强代码的健壮性
printf("动态内存分配失败\n");
exit(0); //退出程序
}
L->next=NULL; //将表置空
LinkList p=L; //创建将结点p并指向L
LinkList q; //创建结点q
//初始化链表
for(int i=1;i<=number;i++){ //循环语句
q=(LinkList)malloc(sizeof(LNode)); //给结点q动态开辟空间
if(q==NULL){ //同理在增强代码健壮性
printf("为结点动态分配空间失败:");
exit(0);
}
p->next=q; // 将结点p的下一个结点指向q
p=q; // 将结点p移动到q的位置
printf("data:");
scanf("%d",&(q->data)); // 给q的data域赋值
q->next=NULL; // 将q的下一个置空
}
}
//该函数的功能为给第location个位置插入元素data
void insertList(LinkList &L,int location,int data)//location表示链表的第location个结点 , data表示为插入的数为data
{
LinkList p=L; //创建将结点p并指向L
LinkList q; //创建结点q
q=(LinkList)malloc(sizeof(LNode)); //为q动态分配内存空间
if(q==NULL){ //同理在增强代码健壮性
printf("为结点动态分配空间失败:");
exit(0);
}
//思考为什么是要找到location-1的位置,将p放到location-1这个位置 可以画图方便理解
for(int i=1;i<=location-1;i++){
p=p->next;
}
q->next=p->next; //将q的指向的下一个结点指向p目前的下一个结点,这样就断开了p和p的下一个结点直接的线
p->next=q; //接着将p的下一个结点指向q这样q就插入进来了
q->data=data; //这里的=后边的data是函数传来的data,别混淆了
printList(L); //打印新的链表
}
//删除链表中的某个值的函数
void deleteList(LinkList &L,int location) //location表示要删除的第某个结点
{
LinkList p=L; //创建将结点p并指向L
LinkList q; //创建结点q
//为什么在q没有分配内存空间时候一样可以使用?
q=(LinkList)malloc(sizeof(LNode));
if(q==NULL){ //同理在增强代码健壮性
printf("为结点动态分配空间失败:");
exit(0);
}
Status data; //用于存储被删除的结点所对应的值,并输出
for(int i=1;i<=location-1;i++){ //还是找位置location-1
p=p->next;
}
q=p->next; //将q放在p的下一个结点
p->next=q->next; //断开p指向q的线,使线指到q的next的位置。相当于把q架空了
q->next=NULL; //使结点q的下一个指向置空
data=q->data; //用data存储将要被删除的结点q中的值
free(q); //释放q
printf("删除的结点的数值为:%d",data);
printf("\n");
printf("删除后的链表为:"); //打印新的链表
printList(L);
}
//查找链表中某个位置结点的值,因为之前的插入和删除操作的使用,链表的值已经变化了,不再是第一你输入的时候的链表值了。所以别混淆了
void seekList(LinkList L,int location) //location表示的是查找的那个元素的位置
{
LinkList p=L; //创建将结点p并指向L
for(int i=1;i<=location;i++){ //遍历链表找到你想操作的那个结点
p=p->next;
}
printf("你所查找的位置的结点的值为:");
printf("%d",p->data); //输出该结点data域中的值
printf("\n");
}
//销毁链表
void destoryList(LinkList &L)
{
LinkList p=L; //到了这里相信大家都发现了这个。每个函数都出来一次,为什么?自己思考下
while(L){ //销毁链表时候要一个结点一个结点的删除,所以这里采用了循环语句
p=L->next; //一个结点一个结点的删除
free(L);
L=p;
}
printf("线性表已被成功销毁\n");
}
//菜单函数,实现各项功能
void Menu(void)
{
LinkList L;
Status a; //用于控制开关
Status number; //用于指示链表中结点的个数
Status location,data; //用于函数传参时候数据的操作
while(1){
printf("\t\t---------实现单链表的创建请按1:---------\n");
printf("\t\t---------插入单链表的结点请按2:---------\n");
printf("\t\t-------删除单链表的某个结点请按3:-------\n");
printf("\t\t---------实现单链表的查找请按4:---------\n");
printf("\t\t---------实现单链表的销毁请按5:---------\n");
printf("\t\t---------------退出请按6:---------------\n");
scanf("%d",&a);
switch(a){
case 1:
printf("请输入链表的结点个数:");
scanf("%d",&number);
creatList(L,number); //创建链表 //在调用函数时参数切不可写成这样creatList(&L,number);注意下初学者比较容易犯错
printf("是否需要打印出链表中的值:(是请按T,否请按N)\n");
char flag; //在判断时候用的变量
scanf("%s",&flag); //scanf("%c",&flag) 会被跳过scanf不信你试试 (*^__^*) ……
if(flag=='T'){
printList(L);
}
else{ //这里的代码是为了增强健壮性,觉得太冗长的人可以注释掉
if(flag=='N'){
break;
}
else{
printf("输入有误请按要求输入\n");
break;
}
}
break;
case 2:
printf("请输入你想在第几个位置插入元素:");
scanf("%d",&location);
printf("\n");
printf("请输入你要插入的元素:");
scanf("%d",&data);
insertList(L,location,data); //插入链表,在第location个位置插入元素data
break;
case 3:
printf("请输入你想删除第几个位置的元素:");
scanf("%d",&location);
deleteList(L,location); //删除链表中的某个位置的元素
break;
case 4:
printf("输入你想查看的第几个结点的值:");
scanf("%d",&location); //查找第location个结点
seekList(L,location); //寻找链表中的某个结点
break;
case 5:
destoryList(L); //使用完了销毁链表
break;
case 6:
exit(0);
break;
}
}
}
//主函数 用来调用子函数
Status main(void)
{
Menu();
system("pause");
return 0;
}