一,创建一个单向链表
引入:情景就是我要存下一大批玩家的数据信息,没有一整块,或者难以找到一整块儿的存储空间,那用链表的话(核心在于一个包含数据域和指针域的结构体,数据域存储玩家信息,指针域存储放下一个玩家的内存的首地址),就相当于把碎片化的内存联系起来达到整体的,连续的效果。
当然,进一步就会强调,头(头指针,存储第一个玩家的首地址),尾(尾节点,尾指针是NULL,什么地方也不指向)。
1,简易版
/*创建一个单向链表1.0*/
#include<stdio.h>
#include<stdlib.h>
/*准备工作,包含数据域和指针域的结构体*/
typedef struct player
{
int account;
char name[20];
int HP;
int power;
int intelligence;
struct player *next;
}PLAYER;
int main()
{
/*一,创建节点*/
PLAYER pl1={1001,"狗兔秃",27778,36669,666};
PLAYER pl2={1002,"富贵儿",999999,666666,888};
PLAYER pl3={1002,"修勾儿",333222,33323,555};
/*二,连接,给指针与赋上后继节点的地址值*/
pl1.next=&pl2;
pl2.next=&pl3;
/*三,确定头指针*/
PLAYER *head=&pl1;/*头指针类型要和节点里的一致*/
/*四,确定尾节点*/
pl3.next=NULL;
/*实际操作*/
printf("%s\n",pl2.name);
printf("%d\n",pl1.account);
printf("%d\n",pl3.power);
return 0;
}
2.简易版(节点极少)+模块化
?:为什么模块化之后要用malloc申请内存
--:因为模块化之后仍然用结构体变量的话,它就变成了局部变量,它随着函数的使用而开始,随着函数的结束而结束,由栈来存储,系统会自动产生和回收这部分内存,也就是说运行完这个函数,里边定义的那些变量就没了。因此,模块化之后,我们用指向结构体变量的指针来操作,给它用malloc申请内存(堆里面的),就不会产生以上的后果,反而得到我们真实想要的效果。
/*创建一个单向链表2.0*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
/*准备工作,包含数据域和指针域的结构体*/
typedef struct player
{
int account;
char name[20];
int HP;
int power;
int intelligence;
struct player *next;
}PLAYER;
PLAYER * Build(void);
int main()
{
PLAYER * head;
head=Build();
/*实际操作*/
printf("The first player's name is: %s\n",head->name);
return 0;
}
PLAYER * Build(void)
{
/*一,创建节点*/
PLAYER *pl1 = (PLAYER *)malloc(sizeof(PLAYER));
PLAYER *pl2 = (PLAYER *)malloc(sizeof(PLAYER));
PLAYER *pl3 = (PLAYER *)malloc(sizeof(PLAYER));
if(pl1!=NULL)
{
pl1->account=1001;
strcpy(pl1->name,"狗兔秃");
pl1->HP=27778;
pl1->power=36669;
pl1->intelligence=666;
}
if(pl2!=NULL)
{
pl2->account=1002;
strcpy(pl2->name,"富贵儿");
pl2->HP=999999;
pl2->power=666666;
pl2->intelligence=888;
}
if(pl3!=NULL)
{
pl3->account=1003;
strcpy(pl3->name,"修勾儿");
pl3->HP=333222;
pl3->power=33323;
pl3->intelligence=555;
}
/*二,连接,给指针与赋上后继节点的地址值*/
pl1->next=pl2;
pl2->next=pl3;
/*三,确定头指针*/
PLAYER *head=pl1;/*头指针类型要和节点里的一致*/
/*四,确定尾节点*/
pl3->next=NULL;
/*最后返回头指针*/
return head;
}
3.复杂一点点+模块化
注:Build这个函数,把节点分成头节点和其他节点进行写入。
malloc申请完内存之后,首先应该判断是否申请到了,也就是malloc是有可能申请不到内存的。
申请到了可以继续操作,没申请到就exit(0)结束函数。
首先我们要定义三个指向结构体的指针变量,head不用说,存放头指针的,最后要作为返回值。
Pt则是我们每次要读入数据的结点的指针,应该指向他们各自的‘首地址’。
prePt的话则是作为一个中介,或者说推进的存在,它总是用来保存链表当前的结点的地址,所以十分重要。
然后先单独给头节点(没有前驱节点)申请内存,头节点申请到了才可以继续下去,然后给头节点读入数据,
对下面操作的准备工作:
(用Pt)给head赋值<=>确定头指针,(用当前Pt)给prePt赋值才能往后推进<=>定下头节点这个格子。
接下来,进入循环,给下一个节点申请内存,判断是否申请成功,
关键理解这一部分的赋值,将新申请的Pt赋给prePt->next(当前的结点的的指针域)<=>填满上一个格子的指针域,再将当前Pt(地址)整体赋给prePt<=>定下一个格子。
最后就是对尾节点的处理了,在循环外,把NULL赋值给Pt->next,作为当前节点(尾节点)的地址,当然赋给prePt也是等价的。
总的来说,最关键的是对赋值操作的理解,赋值操作(抽象化地)分为两类,定格子,以及顶格子里的内容(数据域不在这里说,由键盘输入;指针域,装的是下一个格子的地址,也就是只有走到下一步才能定下上一个格子的指针域这块内容)。还是画图理解吧,比较保险。
/*创建一个单向链表3.0*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
/*准备工作,包含数据域和指针域的结构体*/
typedef struct player
{
int account;
char name[20];
int HP;
int power;
int intelligence;
struct player *next;
}PLAYER;
PLAYER *Build(int num);
int main()
{
int n;
/*打算要多少个节点*/
scanf("%d",&n);
PLAYER * head;
head=Build(n);
/*实际操作*/
printf("The first player's name is: %s\n",head->name);
return 0;
}
PLAYER * Build(int num)
{
/*一,创建节点*/
/*二,连接,给指针与赋上后继节点的地址值*/
/*三,确定头指针*/
/*头指针类型要和节点里的一致*/
/*四,确定尾节点*/
/*最后返回头指针*/
PLAYER *Pt,*prePt,*head;
int i;
Pt=(PLAYER *)malloc(sizeof(PLAYER));
if(Pt!=NULL)
{
scanf("%d%s%d%d%d",&Pt->account,Pt->name,&Pt->HP,&Pt->power,&Pt->intelligence);
head=Pt;
prePt=Pt;
for(i=1;i<num;i++)
{
Pt=(PLAYER *)malloc(sizeof(PLAYER));
if(Pt!=NULL)
{
scanf("%d%s%d%d%d",&Pt->account,Pt->name,&Pt->HP,&Pt->power,&Pt->intelligence);
Pt->next=Pt;
prePt=Pt;
}
else
{
printf("Failed.\n");
exit(0);
}
}
}
else
{
printf("Failed.\n");
exit(0);
}
Pt->next=NULL;
//或者prePt=NULL;
return head;
}
二,增加查找操作
/*创建一个单向链表3.0*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
/*准备工作,包含数据域和指针域的结构体*/
typedef struct player
{
int account;
char name[20];
int HP;
int power;
int intelligence;
struct player *next;
}PLAYER;
PLAYER *Build(int num);/*创建若干个节点的链表*/
PLAYER *FindLast(PLAYER *head);/*通过头指针查找尾节点*/
PLAYER *FindX(PLAYER *head,int index);/*通过头指针查找第x个节点*/
int main()
{
int n,x;
/*打算要多少个节点*/
printf("打算要几个节点?\n");
scanf("%d",&n);
/*查找第x个节点*/
printf("查找第几个节点?\n");
scanf("%d",&x);
/*实际操作*/
printf("Please input the data.\n");
printf("account name HP power inteligence\n");
PLAYER *head=Build(n);
printf("The first player's name is: %s\n",head->name);
/*查找尾节点*/
PLAYER *last=FindLast(head);
printf("The last player's power is: %d\n",last->power);
/*查找第x个节点*/
PLAYER *X=FindX(head,x);
printf("The %dth player's intelligence is: %d\n",x,X->intelligence);
return 0;
}
PLAYER * Build(int num)
{
/*一,创建节点*/
/*二,连接,给指针与赋上后继节点的地址值*/
/*三,确定头指针*/
/*头指针类型要和节点里的一致*/
/*四,确定尾节点*/
/*最后返回头指针*/
PLAYER *Pt,*prePt,*head;
int i;
Pt=(PLAYER *)malloc(sizeof(PLAYER));
if(Pt!=NULL)
{
scanf("%d%s%d%d%d",&Pt->account,Pt->name,&Pt->HP,&Pt->power,&Pt->intelligence);
head=Pt;
prePt=Pt;
}
else
{
printf("Failed.\n");
exit(0);
}
for(i=1;i<num;i++)
{
Pt=(PLAYER *)malloc(sizeof(PLAYER));
if(Pt!=NULL)
{
scanf("%d%s%d%d%d",&Pt->account,Pt->name,&Pt->HP,&Pt->power,&Pt->intelligence);
prePt->next=Pt;
prePt=Pt;
}
else
{
printf("Failed.\n");
exit(0);
}
}
Pt->next=NULL;
return head;
}
PLAYER *FindLast(PLAYER *head)
{
PLAYER *pr;
pr=head;/*存储当前节点*/
/*查找尾节点,当前节点指针域是否为空*/
while(pr->next!=NULL)
{
pr=pr->next;
}
return pr;
}
PLAYER *FindX(PLAYER *head,int index)
{
int i=1;
PLAYER *pr;
pr=head;/*存储当前节点*/
/*查找第x个节点,当前节点指针域是否为空;0-n-1&&1-n,即第一个节点是头节点*/
while(i<index&&pr!=NULL)//pr!=NULL是为了防止越界操作,即index>n的情况,当前的指针域不能是空指针
{
pr=pr->next;
i++;
}
return pr;
}
三,增加插入操作
/*创建一个单向链表3.0*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
/*准备工作,包含数据域和指针域的结构体*/
typedef struct player
{
int account;
char name[20];
int HP;
int power;
int intelligence;
struct player *next;
}PLAYER;
PLAYER *Build(int num);/*创建若干个节点的链表*/
PLAYER *FindLast(PLAYER *head);/*通过头指针查找尾节点*/
PLAYER *FindX(PLAYER *head,int index);/*通过头指针查找第x个节点*/
PLAYER *InsertLast(PLAYER *head);/*向单向链表的尾部插入一个节点*/
PLAYER *InsertX(PLAYER *head,int index);/*向单向链表中某节点后插入一个节点*/
int main()
{
int n,x;
/*打算要多少个节点*/
printf("打算要几个节点?\n");
scanf("%d",&n);
printf("Please input the data.\n");
printf("account name HP power inteligence\n");
/*实际操作*/
/*创建若干节点的链表
PLAYER *head=Build(n);*/
/*查找头节点
printf("The first player's name is: %s\n",head->name);*/
/*查找尾节点
PLAYER *last=FindLast(head);
printf("The last player's power is: %d\n",last->power);*/
/*查找第X个节点
printf("查找第几个节点?\n");
scanf("%d",&x);
printf("The %dth player's intelligence is: %d\n",x,X->intelligence);*/
/*向尾部插入一个节点
printf("向尾部插入一个节点\n");
head=InsertLast(head);*/
/*向某节点后插入一个节点
printf("向哪个节点后插入新的节点?\n");
scanf("%d",&x);
head=InsertX(head,x);*/
return 0;
}
PLAYER * Build(int num)
{
/*一,创建节点*/
/*二,连接,给指针与赋上后继节点的地址值*/
/*三,确定头指针*/
/*头指针类型要和节点里的一致*/
/*四,确定尾节点*/
/*最后返回头指针*/
PLAYER *Pt,*prePt,*head;
int i;
Pt=(PLAYER *)malloc(sizeof(PLAYER));
if(Pt!=NULL)
{
scanf("%d%s%d%d%d",&Pt->account,Pt->name,&Pt->HP,&Pt->power,&Pt->intelligence);
head=Pt;
prePt=Pt;
}
else
{
printf("Failed.\n");
exit(0);
}
for(i=1;i<num;i++)
{
Pt=(PLAYER *)malloc(sizeof(PLAYER));
if(Pt!=NULL)
{
scanf("%d%s%d%d%d",&Pt->account,Pt->name,&Pt->HP,&Pt->power,&Pt->intelligence);
prePt->next=Pt;
prePt=Pt;
}
else
{
printf("Failed.\n");
exit(0);
}
}
Pt->next=NULL;
return head;
}
PLAYER *FindLast(PLAYER *head)
{
PLAYER *pr;
pr=head;/*存储当前节点*/
/*查找尾节点,当前节点指针域是否为空*/
while(pr->next!=NULL)
{
pr=pr->next;
}
return pr;
}
PLAYER *FindX(PLAYER *head,int index)
{
int i=1;
PLAYER *pr;
pr=head;/*存储当前节点*/
/*查找第x个节点,当前节点指针域是否为空;0-n-1&&1-n,即第一个节点是头节点*/
while(i<index&&pr!=NULL)//pr!=NULL是为了防止越界操作,即index>n的情况,当前的指针域不能是空指针
{
pr=pr->next;
i++;
}
return pr;
}
PLAYER *InsertLast(PLAYER *head)
{
PLAYER *prePt,*Pt;/*这俩的作用跟创建时的是基本一样的*/
Pt=(PLAYER *)malloc(sizeof(PLAYER));/*给新插入格子申请空间*/
scanf("%d%s%d%d%d",&Pt->account,Pt->name,&Pt->HP,&Pt->power,&Pt->intelligence);/*新插入格子的数据域*/
/*下面的赋值是关键,不多说,画图理解吧*/
prePt=FindLast(head);/*首先找到尾节点,并赋值给中介格子(存放当前节点,对此处而言是尾节点,的地址)*/
prePt->next=Pt;/*把这个格子的指针域的值改成新插入的结点的地址*/
Pt->next=NULL;/*把新插入的格子的指针域调成空,不指向任何区域*/
return head;/*最后别忘了返回头指针,链表的‘车头’必须时刻清楚*/
}
PLAYER *InsertX(PLAYER *head,int index)
{
PLAYER *prePt,*Pt;/*这俩的作用跟创建时的是基本一样的*/
Pt=(PLAYER *)malloc(sizeof(PLAYER));/*给新插入格子申请空间*/
scanf("%d%s%d%d%d",&Pt->account,Pt->name,&Pt->HP,&Pt->power,&Pt->intelligence);/*新插入格子的数据域*/
prePt=FindX(head,index);
if(prePt!=NULL)
{
/*可不能颠倒顺序,颠倒了就相当于自查,不能丢失后继结点的地址*/
Pt->next=prePt->next;
prePt->next=Pt;
}
else/*如果没找到这个节点*/
{
printf("There is no such node\n");
}
return head;
}
四,增加删除操作
/*创建一个单向链表3.0*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
/*准备工作,包含数据域和指针域的结构体*/
typedef struct player
{
int account;
char name[20];
int HP;
int power;
int intelligence;
struct player *next;
}PLAYER;
PLAYER *Build(int num);/*创建若干个节点的链表*/
PLAYER *FindLast(PLAYER *head);/*通过头指针查找尾节点*/
PLAYER *FindX(PLAYER *head,int index);/*通过头指针查找第x个节点*/
PLAYER *InsertLast(PLAYER *head);/*向单向链表的尾部插入一个节点*/
PLAYER *InsertX(PLAYER *head,int index);/*向单向链表中某节点后插入一个节点*/
PLAYER *Delete(PLAYER *head,int index);/*删除哪个节点*/
int main()
{
int n,x;
/*打算要多少个节点*/
printf("打算要几个节点?\n");
scanf("%d",&n);
printf("Please input the data.\n");
printf("account name HP power inteligence\n");
/*实际操作*/
/*创建若干节点的链表
PLAYER *head=Build(n);*/
/*查找头节点
printf("The first player's name is: %s\n",head->name);*/
/*查找尾节点
PLAYER *last=FindLast(head);
printf("The last player's power is: %d\n",last->power);*/
/*查找第X个节点
printf("查找第几个节点?\n");
scanf("%d",&x);
printf("The %dth player's intelligence is: %d\n",x,X->intelligence);*/
/*向尾部插入一个节点
printf("向尾部插入一个节点\n");
head=InsertLast(head);*/
/*向某节点后插入一个节点
printf("向哪个节点后插入新的节点?\n");
scanf("%d",&x);
head=InsertX(head,x);*/
/*删除哪个节点
printf("删除哪个节点");
scanf("%d",&x);
head=Delete(head,x);*/
return 0;
}
PLAYER * Build(int num)
{
/*一,创建节点*/
/*二,连接,给指针与赋上后继节点的地址值*/
/*三,确定头指针*/
/*头指针类型要和节点里的一致*/
/*四,确定尾节点*/
/*最后返回头指针*/
PLAYER *Pt,*prePt,*head;
int i;
Pt=(PLAYER *)malloc(sizeof(PLAYER));
if(Pt!=NULL)
{
scanf("%d%s%d%d%d",&Pt->account,Pt->name,&Pt->HP,&Pt->power,&Pt->intelligence);
head=Pt;
prePt=Pt;
}
else
{
printf("Failed.\n");
exit(0);
}
for(i=1;i<num;i++)
{
Pt=(PLAYER *)malloc(sizeof(PLAYER));
if(Pt!=NULL)
{
scanf("%d%s%d%d%d",&Pt->account,Pt->name,&Pt->HP,&Pt->power,&Pt->intelligence);
prePt->next=Pt;
prePt=Pt;
}
else
{
printf("Failed.\n");
exit(0);
}
}
Pt->next=NULL;
return head;
}
PLAYER *FindLast(PLAYER *head)
{
PLAYER *pr;
pr=head;/*存储当前节点*/
/*查找尾节点,当前节点指针域是否为空*/
while(pr->next!=NULL)
{
pr=pr->next;
}
return pr;
}
PLAYER *FindX(PLAYER *head,int index)
{
int i=1;
PLAYER *pr;
pr=head;/*存储当前节点*/
/*查找第x个节点,当前节点指针域是否为空;0-n-1&&1-n,即第一个节点是头节点*/
while(i<index&&pr!=NULL)//pr!=NULL是为了防止越界操作,即index>n的情况,当前的指针域不能是空指针
{
pr=pr->next;
i++;
}
return pr;
}
PLAYER *InsertLast(PLAYER *head)
{
PLAYER *prePt,*Pt;/*这俩的作用跟创建时的是基本一样的*/
Pt=(PLAYER *)malloc(sizeof(PLAYER));/*给新插入格子申请空间*/
scanf("%d%s%d%d%d",&Pt->account,Pt->name,&Pt->HP,&Pt->power,&Pt->intelligence);/*新插入格子的数据域*/
/*下面的赋值是关键,不多说,画图理解吧*/
prePt=FindLast(head);/*首先找到尾节点,并赋值给中介格子(存放当前节点,对此处而言是尾节点,的地址)*/
prePt->next=Pt;/*把这个格子的指针域的值改成新插入的结点的地址*/
Pt->next=NULL;/*把新插入的格子的指针域调成空,不指向任何区域*/
return head;/*最后别忘了返回头指针,链表的‘车头’必须时刻清楚*/
}
PLAYER *InsertX(PLAYER *head,int index)
{
PLAYER *prePt,*Pt;/*这俩的作用跟创建时的是基本一样的*/
Pt=(PLAYER *)malloc(sizeof(PLAYER));/*给新插入格子申请空间*/
scanf("%d%s%d%d%d",&Pt->account,Pt->name,&Pt->HP,&Pt->power,&Pt->intelligence);/*新插入格子的数据域*/
prePt=FindX(head,index);
if(prePt!=NULL)
{
/*可不能颠倒顺序,颠倒了就相当于自查,不能丢失后继结点的地址*/
Pt->next=prePt->next;
prePt->next=Pt;
}
else/*如果没找到这个节点*/
{
printf("There is no such node\n");
}
return head;
}
PLAYER *Delete(PLAYER *head,int index)
{
//删除过程中,我们需要接触三个地址
PLAYER *Pt=NULL,*prePt=NULL;
prePt=FindX(head,index-1);
//找到一个节点/地址实际上是获得了两个地址,我们要做的就是取到两个我们需要的地址,问题是取当前节点还是它的前驱节点,肯定不能取后继节点,因为它只有一个我们需要的值
//但是取当前节点的话,我们得查找两次,因为由它指向的地址所指的是我们所不需要的,所以要减少查找次数且不易出错的话,还是取前驱节点好
Pt = prePt->next;
if(Pt!=NULL)//我甚至觉得判断prePt更好
{
prePt->next=Pt->next;
free(Pt);
}
else
{
printf("There is no such node\n");
}
return head;
}
/************************************************************************/
数据结构之后
创建结点
/*****创建结点*****/
typedef int datatype;
typedef struct link_node
{
datatype info;
struct link_node *next;
}node;
typedef node *linklist;
头插创建单链表
/*******头插:新结点往前加********/
linklist creatbystack()
{
linklist head=NULL,p;
datatype x;
while(x!=0)
{
p=(linklist)malloc(sizeof(node));
p->info=x;
p->next=head;
head=p;
scanf("%d",&x);
}
return head;
}
尾插创建单链表
/*******尾插:队列方式建立********/
linklist ctreatbyqueue()
{
linklist head=NULL,r=NULL,s;
//r为链尾指针,每次新的节点链在r的后面,使其成为新的表尾结点
datatype x;
scanf("%d",&x);
while(x!=0)
{
s=(linklist)malloc(sizeof(node));
s->info=x;
if(head==NULL) head=s;//新结点插入新表
else r->next=s;
r=s;
scanf("%d",&x);
}
if(r!=NULL) r->next=NULL;
return head;
}
输出(不带头结点)
/************打印结点值************/
void print(linklist head)
{
linklist p;
p=head;
while(p)
{
printf("%5d",p->info);
p=p->next;
}
printf("\n");
}
释放(不带头结点)
/**********释放不带头结点的单链表****/
void delList(linklist head)
{
linklist p=head;
/*一个一个结点释放*/
while(p!=NULL)
{
head=p->next;
free(p);
p=head;
}
}
删除第一个值为x的结点(不带头结点的链表)
linklist delx(linklist head,datatype x)
{
linklist pt=head,pre=NULL;
while(pt!=NULL&&pt->info!=x)
{
pre=pt;
pt=pt->next;
}
if(pt!=NULL)//若进入以上循环,出循环时,pre指向x前的节点,pt指向值为x的结点
{
if(pre==NULL)//没进循环,第一个结点的值就是x
head=pt-next;
else//一般情况,值为x的结点在中间,包括值在最后一个结点的情况
pre->next=pt->next;
free(pt);
}
return head;//如果这个链表的结点里压根没有x,直接这一步就好了
}
删除所有值为x的结点(不带头结点)
linklist delallx(linklist head,datatype x)
{
linklist pre=NULL,pt=head;
while(pt)
{
while(pt!=NULL&&pt->info!=x)//查找值为x的结点
{
pre=pt;
pt=pt->next;
}
if(pt!=NULL)//删除,释放这个节点
{
if(pre==NULL)
{
head=pt->next;
free(pt);
pt=head;//要接上,pt要继续用
}
else
{
pre->next=pt->next;
free(pt);
pt=pre->next;//接上,pt要有指向
}
}
}
return head;
}
逆序链表(原地倒置)
法一
linklist reverse1(linklist head)
{
linklist s,r;
s=head;
head=NULL;
while(s)
{
r=s;
s=s->next;
r->next=head;
head=r;
}
return head;
}
法二
void reverse2(linklist *head)
{
linklist s,r;
s=*head;
*head=NULL;
while(s)
{
r=s;
s=s->next;
r->next=*head;
*head=r;
}
}
插入值为x的链表(顺序为升序)
linklist insert(linklist head,datatype x)
{
linklist p,r,s;//s是因为插入了一个新的结点,所以要创建一个新的
p=NULL;
r=head;
while(r!=NULL && r->info < x)//查找值为x的结点
{
p=r;
r=r->next;
}
s=(linklist)malloc(sizeof(node));//一般情况s插在r前面(x<=r->info)
s->info=x;
if(p!=NULL)//一般情况
{
s->next=r;
p->next=s;
}
else//特殊情况:x<=第一个结点的值,直接插在前面,涉及head
{
s->next=head;
head=s;
}
return head;
}