在前一段学习的数据结构中,我感觉对于链表的知识有些遗忘,其实终究看来是C语言知识的遗忘,下面我将借助以下几个详细的例子,来看看我们相关的的结构体指针与链表。其实在我看来,我一直感觉结构体是类的雏形,没有成型的类,备受限制而又非常开放的类。下面我们还是以链表为例:
单链表:
(1)头指针变量head──指向链表的首结点。
(2)每个结点由2个域组成:
数据域──存储结点本身的信息。
指针域──指向后继结点的指针。
(3)尾结点的指针域置为“NULL(空)”,作为链表结束的标志。
对于数据的操作无外乎是由“增,删,改,查”四项功能来组合而成的,真有点像人与“衣,食,住,行”的关系。
对链表的基本操作
对链表的基本操作有:创建、遍历(查找)、插入、删除等。
(1)创建链表是指,从无到有建立起链表,即往空链表中依次插入若干结点,并保持结点之间的前驱和后继关系。
(2)遍历链表是指,查找链表上所有结点。
(3)插入结点是指,在结点ki-1与ki之间插入一个新的结点k’,使线性表的长度增1,且ki-1与ki的逻辑关系发生如下变化:
插入前,ki-1是ki的前驱,ki是ki-1的后继;插入后,新插入的结点k’成为ki-1的后继、ki的前驱。
(4)删除结点是指,删除结点ki,使线性表的长度减1,且ki-1、ki和ki+1之间的逻辑关系发生如下变化:
删除前,ki是ki+1的前驱、ki-1的后继;删除后,ki-1成为ki+1的前驱,ki+1成为ki-1的后继。
实例展示:
struct node//创建结构体
{
char no[7]; /*学号*/
int score; /*成绩*/
struct node *next; /*指针域*/
};
下面我们来创建一个新链表
基本思路:
首先向系统申请一个结点的空间,然后输入结点数据域的(2个)数据项,并将指针域置为空(链尾标志),最后将新结点插入到链表尾。对于链表的第一个结点,还要设置头指针变量。
另外,案例代码中的3个指针变量phead、pnew和ptail的说明如下:
(1)phead──头指针变量,指向链表的第一个结点,用作函数返回值。
(2)pnew──指向新申请的结点。
(3)ptail──指向链表的尾结点,用ptail->next=pnew,实现将新申请的结点,插入到链表尾,使之成为新的尾结点。
/*CreateTab()函数: 创建链表*/
/*形参:int n 结点的个数*/
/*返回值:返回链表的头指针*/
struct node *CreateTab( int n )
{ int i; /*循环变量*/
struct node *pnew, *phead=NULL, *ptail;
/*(1)phead──头指针变量,指向链表的第一个结点,用作函数返回值。
(2)pnew──指向新申请的结点。
(3)ptail──指向链表的尾结点*/
for(i=0;i<n;i++) /*用循环建立n个结点,注意第3个表达式为空*/
{
/*申请一个新结点的空间*/
pnew=(struct node *)malloc(sizeof(struct node));
/*1、输入结点数据域的各数据项*/
printf("Input the ID of student No.%d(6 bytes): ", i+1);
scanf("%6s", pnew->ID);
printf("Input the score of the student No.%d: ", i+1);
scanf("%d", &pnew->score);
/*2、置新结点的指针域为空*/
pnew->next=NULL;
/*3、将新结点插入到链表尾,并设置新的尾指针*/
if(i==0)
phead=pnew; /*是第一个结点, 置头指针*/
else
ptail->next=pnew; /*非首结点, 将新结点插入到链表尾*/
ptail=pnew; /*设置新的尾结点*/
}
return(phead);/*返回链表头指针*/
}
/*遍历链表*/
void FindTab(struct node *p)
{
printf("List of Student's ID&Score/n");
printf("=========================/n");
printf("ID Score/n");
while(p!=NULL)
{ printf("%s %d/n",p->ID,p->score);
p=p->next;
}
}
对链表的插入操作
编写一个insert()函数,完成在单链表的第i个结点后插入1个新结点的操作。当i=0时,表示新结点插入到第一个结点之前,成为链表新的首结点。
基本思路:
通过链表的头指针,首先找到链表的第一个结点;然后顺着结点的指针域找到第i个结点,最后将新结点插入到第i个结点之后。
/*函数功能:在单链表的第i个结点后插入1个新结点*/
/*函数参数:phead为链表的头指针,pnew指向要插入的新结点,i为结点索引号*/
/*函数返回值:链表的头指针*/
struct node *insert(struct node *phead, struct node *pnew, int i)
{ struct node *pointer; /*将新结点插入到链表中*/
if(phead==NULL) /*将新结点插入到1个空链表中*/
phead=pnew, pnew->next=NULL;
else /*非空链表*/
if(i==0) /*使新结点成为链表新的首结点*/
pnew->next=phead, phead=pnew;
else /*其他位置*/
{ pointer=phead;/*查找单链表的第i个结点(pointer指向它)*/
for(; pointer!=NULL && i>1; pointer=pointer->next, i--) ;
if(pointer==NULL) /*越界错*/
printf("Out of the range, can’t insert new node!/n");
else /*一般情况:pointer指向第i个结点 */
pnew->next=pointer->next, pointer->next=pnew;
}
return(phead);
}
对链表的删除操作
编写一个delete()函数,删除链表的指定结点。
基本思路:
通过链表的头指针,首先找到链表的结点;然后顺着结点的指针域找到第i个结点,最后将新结点插入到第i个结点之后。
/*函数功能:删除链表的某个结点*/
/*函数参数:phead为链表的头指针,pdel指向要删除的结点*/
/*函数返回值:若删除成功返回1,否则返回0*/
int delete(struct node *phead, struct node *pdel)
{ struct node *temp;
if (phead==pdel) /*如果要删除的是头结点*/
phead=phead->next; /*将头结点指向它的下一个结点*/
else /*如果不是删除头结点*/
{ temp=phead;/*暂存头指针到temp*/
while(temp->next != pdel) /*查找要删除的结点*/
{ if(temp->next != NULL)
temp=temp->next;
else return(0);/*如果没找到要删除的结点*/
}
temp->next=pdel->next;/*找到要删除的结点后*/
}
free(pdel);/*释放被删除结点的内存空间*/
return(1);
}