单链表的基本操作主要有;①创建链表;②输出链表;③査我结点;④插入结点,⑤鹏除结点;⑥重组链表。下面分别进行介绍。
一.创建链表
创建链表是指在程序运行时,进行动态内存分配,创建若千个结点,并把这些结点连接成串,形成一个链表。在进行动态内存分配时,需要使用在(2)介绍的内存管理函数malloc()和 free()。
(1)创建链表的步骤如下:
(1)定义链表的数据结构,创建一个空的头指针。
(2)使用malloc()函数为新结点分配内存空间。
(3)将数据读入到新结点的数据域,并将该结点的指针域置为NULL。
(4)若头指针为空,则使头指针指向该新结点;若头指针为非空,则将新结点连接到链表
中;可以连接到链首或连接到链尾。
(5)判断是否有后续结点,若有,则转向(2).否则,链表创建结束。
(2)尾插法
例 接收从键盘输入的一行字符,每接收一个字符后,申请一个新结点保存该字符,每次产生的新结点连接到链尾。
struct link
{
char ch;
siruct link *next;
}
//由于每次产生的新结点总是连接到链尾,则需要一个尾指针p2来指示链尾,头指针b指向链首。创建一个新结点p1时,若头指针为空,说明此时链表是空的,则应该使头指针指向新创建的第一个结点,即“h=p1”;若头指针非空,则新结点应该连接到链尾,即“p2->next=p1”。然后修改尾指针,使其指向链尾,即“p2=p1”。
//创建单链表的函数structlink*create()代码如下:
struct link * create()
{
char ch;
struct link *pl, *p2,*h=NULL;
while((ch= getchar( ))!='\n'){
pl=(struet link * )malloc(sizeof(struct link)):
pl->ch=ch;
if(h==NULL)
h=p2=pl;
else
(p2->next=pl;
p2-pl;
p2->next=NULL;
return h;
这里要学会模块化处理链表的复杂数据,其中有指针的运用容易与赋值、地址混淆。
(3)头插法实例
由于每次产生的新结点总是连接到链首,则只需要一个头指针h用来指示链首即可创建一个新结点p时,将新结点连接到链首,即“p->next=h”。然后修改头指针h,使其指向链首,即“h=p”。
struct |ink * create( )
{
char ch;
struct link * h=NULL, * p:
while((cb=getchar())!=\\n')
{
{p=(struct link *)malloc(sizeof(struct link));
p->ch=ch;
p->next=h;
h=p;
}
return h;
}
- 逆序的方式逐个添加到链表中,最后返回创建好的链表的头指针。
p->ch = ch;
将读取的字符存入新节点的数据域。p->next = h;
将新节点的next
指针指向当前的头节点,实现逆序插入。h = p;
更新头指针h
为新创建的节点,使新节点成为链表的新头部
二.输出链表
(1)链表的遍历步骤
输出链表是指从头到尾输出链表中各个结点的数据信息,步骤如下
(1)找到链表的头指针。
(2)设置一个临时结点P,使其指向头指针所指向的结点。
(3)判断是否到链尾,若是,则链表输出结束;否则,输出结点p的数据域信息
(4)使p指向其下一个结点,转向(3)
(2)实例
void printink(struet link *h)
{
stract link *p
if(h==NULL)(printf("h is empty!\n")ireturn;}
p=h:
while(p!=NULL)
(printf("%c",p->ch);
p=p->next;
printf("\n");
}
这里需要注意的是,链表的各结点在内存中可能不是连续存放的,因此不能使用p++来寻找下一个结点。
三.查找结点
(1)查找结点步骤
在单链表中查找指定的结点时,需要从头指针开始,顺序向后查找,直到找到或者到达链尾。步骤如下:
(1)找到链表的头指针 h,使p指向h。
(2)判断结点p的数据域值是否等于要查找的内容,若是,则输出结点p在链表中的位置;若不是,转向(3)。
(3)使p指向下一个结点,判断是否到达链尾,若是,则结束;否则转向(2)。
(2)实例
void search(struct link xh)
{
struct link * p;
char x,m='n';
int id=l;
p=h;
x=getchar();
while(p!=NULL)
{
if(p->ch==x)
{ printf("\'%c \' position is %d\n",x, id);
m='y';
}
p=p->next;
id++;
}if(m=='n')printc"'%c\'is not find!n",x):
print£( "\n" );
}
四.插入结点
链表的插入是指将一个结点插人到一个已有的链表中。这个操作需要确定要插入的位置以及实现正确的插入,插入的原则是先连后断。假设要将结点p插人到结点q和结点r之间,则需要先将结点p与结点r连接(即p>nex1=r),然后将结点q与结点r断开,并使结点q与结点p相连(即q->next=p)。
(1)步骤如下:
(1)找到要插人位置的前趋结点q。
(2)将要插入的结点p的指针域指向结点q的后继结点r。(3)便结点q的指针域指向结点P。(4)判断是否还有要插人的结点,若有,转向(1);否则,结束。
(2)图解实例
struct link *insert(struct link x h)
/*插入结点*/
struct link * p,* q;
inti-0;
q=h;
while(q!-NULL)
{
p=(struct link *)malloc(sizeof(struct link));
/*创建新结点*
p->ch= '0'+(++i);
p->next=q->next;
q->next=p;
q=p->next;
}
return h;
}
五.删除结点
删除结点是指从链表中删除一个或多个指定的结点,并使其余的结点重新连接形成链表,删除的原则是先连后删。
假设p为要删除的结点,q为p的前趋结点,则若要从链表中删除结点p,需要先使结点q指向结点p的后继结点(即q->next=p->next),然后释放结点p占用的内存空间(即
free(p))。
(1)步骤如下:
(1)找到要删除的结点P;(2)若p是链表中的第一个结点,则修改头指针h,使h指向p的后继结点;否则找到要删除结点p的前趋结点q,使q的指针域指向p的后继结点;
(3)释放结点p占用的内存空间;
(4)判断是否还有要删除的结点,若有,转向(1);否则,结束。
(3)从链表中删除一个结点的过程如图
(4)实例
struct link *delel(struct link*h)
{
strucl link *p, *q,* r;
p=q=h;
while(p!=NULL)
{ if(p->ch%2==1)
{ if(p==h)
{
h=p->next;
r=p;
p=q=h;
}
else
{
q->next=p->next;
r-p;
p=q->next;
}
r->next-NULL;
free(r);
}
else
{
fq=p;
p=p->next;
}
return h;
}
六.重组链表
对链表进行重组的操作是指将链表中的结点按照某个规则重新排列形成新的链表,如倒置、排序、合并等。