写在前面
1. "->"这个符号表示访问结构成员
p->mouth p是地址,mouth是p的一个成员
2. p->next 表示的是p指向当前结点的指针域
一.链表的结构
1.链表的实质:动态储存分配的数据结构,是一组节点的序列
2..链表结构示意图
从图中我们可以看到,此链表共有四个元素,其中每一个元素我们把它称为一个节点,而每一个节点则又由两部分组成。其中第一个部分我们把它称为数据域,它所储存的内容是变量值;而第二个部分我们把它称为指针域,其所储存的内容是直接后继储存位置(就是链表下一个节点的储存位置,它实际上就是一个指针)。
二.链表的操作
1.创建链表
#include<stdio.h>
define N 4
main()
{
struct node
{
char ch;
struct node*next;
}*first,*last,*p;
int i; char ch;
(struct node*)malloc(sizeof(struct(node));
ch=getchar();p->ch=ch;p->next=NULL;
first=last=p;
for(i=2;i<=N;i++)
{
ch=getchar();
p=(struct node*)malloc(sizeof(struct node));
p->ch=ch;p->next=NULL;
last->next=p;last=p;}
p=first;//指向表头
while(p!=NULL)
{putchar(p->ch);putchar('');p=p->next;}
}
可以看出这一部分是在创建链表
int i; char ch;
(struct node*)malloc(sizeof(struct(node));
ch=getchar();p->ch=ch;p->next=NULL;
first=last=p;
for(i=2;i<=N;i++)//N为链表结点个数
{
ch=getchar();
p=(struct node*)malloc(sizeof(struct node));
p->ch=ch;p->next=NULL;
last->next=p;last=p;}
我们可以把它分成三个部分
第一部分:开辟第一个结点
int i; char ch;
(struct node*)malloc(sizeof(struct(node));
ch=getchar();p->ch=ch;p->next=NULL;
first=last=p;
第一步:申请内存
(struct node*)malloc(sizeof(struct(node));
第二步:为第一个结点数据域赋值
第三步:将第一个结点的指针域设为NULL
p->next=NULL;
第二部分:开辟第二个结点
第一步:申请内存
p=(struct node*)malloc(sizeof(struct node));
p->ch=ch;p->next=NULL;last->next=p;last=p;}
其中第一行利用了内存申请函数malloch申请到了一段新的储存空间,并改变p的指向,使其指向这段新的空间
第二步:为第二个节点的数据域赋值
p->ch=ch;
于是第二个结点的数据域便获得了刚刚从键盘上输入的字符
第三步:将第二个结点的指针域设为NULL
p->next=NULL;
第三部分:构建结点之间的联系
第一步:将第一个结点指针域指向第二个节点的储存空间
last->next=p;
由于p的指向没有改变,所以这一步会构建起当前节点指针域与下一节点储存空间之间的关系
第二步:改变last指向,使其指向当前最后一个节点
last=p
最后再改变last的指向,使其指向下一结点,也就是当前链表的最后一个结点
总结
不难看出,创建链表就是一个不断循环的过程,我们先将p结点处的指针域设为NULL,当创立下一结点时,再将当前结点指针域内的NULL用下一结点的储存地址替换掉,随后将下一结点指针域设为NULL。这样就完成了一次循环。
2.检索链表
#include<stdio.h>
define N 4
main()
{
struct node
{
char ch;
struct node*next;
}*first,*last,*p;
//上方的这一部分为定义一个结构体
int i; char ch;
(struct node*)malloc(sizeof(struct(node));
ch=getchar();p->ch=ch;p->next=NULL;
first=last=p;
for(i=2;i<=N;i++)
{
ch=getchar();//从键盘输入一个字符
p=(struct node*)malloc(sizeof(struct node));
p->ch=ch;p->next=NULL;last->next=p;last=p;}
p=first;//指向表头
while(p!=NULL)
{putchar(p->ch);putchar('');p=p->next;}
}
还是同样的程序,但这次的关键部分在于如下这四行代码
p=first;//指向表头
while(p!=NULL)
{putchar(p->ch);putchar('');p=p->next;}
}
先将p指向表头为位置,由于链表中最后一个结点的指针域为NULL.所以如果p!=NULL。就代表后面还有结点,反之则是已经到达最后一个结点。
第一步:将当前结点中数据域的值输出
第二步:让p指向下一节点
第三步:继续循环,直到p指向最后一个结点
3.插入结点
struct node
{char ch;
struct node *next;
};
void insertafternode(struct node *pafter,struct node *ponde)
{
pnode->next=pafter->next;
pafter->next=pnode;
}
上方的程序可将pnode插入pafter之后
具体原理如下图所示
可以看到,这里pafter指向的是a所在的节点
这时pafter的下一个结点即为b所在的结点
4.删除结点
p=head;
while(p!=NULL && p->next!=pnode) p=p->next;//找到待删除的结点
if(p!=NULL)
{
p->next=pnode->next;
free(pnode);
}
return head;
}
具体原理如下