链表就是很多个结构体通过地址相关联(至少我目前了解到的是这样),每个结构体里由两部分:一是数据,二是下一个结构体的地址,于是就可以通过一个结构体获取下一个结构的地址,然后通过这个地址访问它的数据,再获取下一个地址,往复。形成一种链状数据结构,如图:
1. 先看我最先接触到的一种写法(无头&&尾插法),看题:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct node{ //节点结构体
int data;
struct node *next; //记录下一节点地址
};
int main()
{
int n,i;
struct node *head,*p,*q; //定义节点结构体类型的指针
//*head记录链表起点指针,*p记录当前指针,*q记录上一次位置指针
while(scanf("%d",&n)!=-1)
{
for(i=0;i<n;i++) //建立n个节点的链表
{
p=(struct node *)(malloc(sizeof(struct node))); //动态申请空间得到空间地址,并将之强
//制类型转换为节点结构体类型指针,赋给p
scanf("%d",&p->data); //其实是&(p->data),这里不能再用".",因为p是地址
if(i==0) //第一次操作,先把第一个地址给head,q保存上一次指针(目前在head上)
head=q=p;
else //后面的操作就是把上一节点的next赋值为当前p(第一次先跳过)
q->next=p;
p->next=NULL; //把当前指针的next赋值为空,其实是为尾部next为空做贡献
q=p; //记录一下本次循环得到的指针,p马上要被覆盖
}
p=head;
while(p!=NULL) //可以直接while(p)
{
if(i==n-1) printf("%d\n",p->data);
else printf("%d ",p->data);
p=p->next;
}
}
}
如果还不是不能理解,跟着循环自己画一下链状图应该就差不多了,逻辑和我给出的第一张图一样。
可以发现这种写法"无头",因为head所指向的节点上也有数据信息,相当于普通的节点。"有头"就是head直接指向的节点上只有next。其次是"尾插",因为新的节点总是跟在原来节点的后面,在尾部插入)。
2. 有头&&头插法(感觉头插法不是很好理解,最好先分析一下代码,画张图,否则可能还是不懂,画完再看注释):
看代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct node{
int data;
struct node *next;
};
int main()
{
int n,i;
struct node *head,*p,*q;
while(scanf("%d",&n)!=-1)
{
head=(struct node *)(malloc(sizeof(struct node))); //先申请了一个头节点,没有赋数据
head->next=NULL; //这个千万别忘了,否则最后一个节点不是NULL
for(i=0;i<n;i++)
{
p=(struct node *)(malloc(sizeof(struct node))); //申请新节点
scanf("%d",&p->data); //节点赋数据
p->next=head->next; //新节点把head的next放到自己后面(自己的next上),再让head指向自己
head->next=p; //就相当于新节点把head的连接的那一条链甩到自己身后,然后自己贴上去
//如果理解我的意思的话不用背也应该知道该怎么写了
}
p=head->next;
while(p!=NULL)
{
if(i==n-1) printf("%d\n",p->data);
else printf("%d ",p->data);
p=p->next;
}
}
}
看的出来每一次有新数据都是插在head和head指向的p中间(头插),最后head指向的是最后一次生成的p,形成了head->p3->p2->p1这样的结构,按照正向输出的话整张链表是倒序的。
3. 链表的基本操作:
输出:上面一直在用,就不说了。
增加:记录当前指针p和上一次指针q,找到位置时q->next=t; t->next=p;即可,但注意有头与无头时头部和尾部插入的处理。
删除:记录当前指针p和上一次指针q,找到时q->next=p->next即可,注意有头和无头时删除头节点的处理。
修改:记录当前指针p,找到修改即可。
查询:记录当前指针p,判断是否符合条件。
排序:
//1. 冒泡排序,交换链表单元上的值
p=q=head;
while(p)
{
q=p->next;
while(q)
{
if(q->num<p->num)
{
t=q->num;
q->num=p->num;
p->num=t;
}
q=q->next;
}
p=p->next;
}
//2. 有序插入
//本题要求降序排列
head=(struct stu *)malloc(sizeof(struct stu));
head->next=NULL;
for(i=0;i<n;i++)
{
p=(struct stu *)malloc(sizeof(struct stu));
scanf("%u %s %lf %lf %lf",&p->num,p->name,&p->classa,&p->classb,&p->classc);
p->sum=(p->classa+p->classb+p->classc);
p->aver=(p->sum)/3.0;
if(i==0)
{
head->next=p;
p->next=NULL;
q=p;
}
else
{
tq=tp=head->next;
while(tp!=NULL && (tp->sum)>(p->sum)) //找到第一个小于p的位置
{
tq=tp;
tp=tp->next;
}
if(tp==head->next){
t=head->next;
head->next=p;
p->next=t;
q=t;
}else if(tp==NULL){
q->next=p;
p->next=NULL;
q=p;
}else{
t=tq->next;
tq->next=p;
p->next=t;
}
}
}
逆序:
方法一:新建链表,用头插法将原表数据复制到新链表。
方法二:对原链表进行操作,比较难理解,自己最好画图看一下。
#include <stdio.h>
#include <stdlib.h>
struct NUM{
int data;
struct NUM *next;
};
int main()
{
int n,t,i; //用尾插法创建有头链表1-
struct NUM *head,*p,*q;
while(scanf("%d",&n)!=-1)
{
head=(struct NUM *)malloc(sizeof(struct NUM));
head->next=NULL;
for(i=0;i<n;i++)
{
scanf("%d",&t);
p=(struct NUM *)malloc(sizeof(struct NUM));
p->data=t;
if(i==0) head->next=p;
else q->next=p;
p->next=NULL;
q=p;
} //链表创建结束-1
p=head->next; //链表逆序2-
while(p->next)
{
q=p->next; //取当前节点的下一个节点
p->next=q->next; //当前节点直接连接到下一个节点的下一个节点
q->next=head->next; //于是就有一个节点被空出来,把这个节点的连接到当前节点
head->next=q; //head指向被空出来的那个节点
} //链表逆序-2
p=head->next;
while(p)
{
printf("%d ",p->data);
p=p->next;
}
}
}
不大明白的话可以看下图:
简单的说一下,这种方法就是从第一个节点开始,让第一个节点直接连到第三个节点,抽出第二个节点,让它连到第一个节点,最后把它放在head后面。随着操作,第一个节点的在链表中的实际位置不断后移(代码中的p就一直是最初的第一个节点),继续用上述方法操作,直到最后一次操作完成后原来第一个节点(p)的next=NULL即它后面没有数的时候。
如果有问题的话,欢迎留言。