在链表中,有一个头指针变量,用这个指针变量保存一个地址,该地址为变量的地址,也就是说,头指针指向一个变量,这个变量称为元素,每一个元素包含俩个部分:数据部分和指针部分。数据部分用来存放数据,指针部分用来指向下一个元素。最后一个元素的指针指向NULL,表示指向的地址为空。链表中的每一个结点包括了数据和指向下一个结点的指针,如果在链表中,每个结点只有一个指针,且所有节点都是单线联系,除了末尾结点指针为空外,每个结点的指针都指向下一个节点,一环扣一环形成一条线性链,称此链表为线性链表或单链表。
单链表的建立
链表的每个结点都必须包括数据域和指针域,即每个结点 要包含不同类型的数据,因此结构体的数据类型选择结构体类型,可包含多个多种类型的成员,其中必须有一个成员的类型是指向本结构体的指针类型。
例如,建立一个链表表示一个班级,其中,链表的结点表示学生,它的结点结构体类型如下
struct student
{
char cName[20]; /*姓名*/
int iNumber; /*学号*/
struct student *next; /* next的类型是指向本结构体类型的指针类型*/
}
在next成员定义中,引用了本结构体类型。也就是说类型定义中采用了递归。
然后,定义一个creat函数,用来创建链表,该函数将会返回链表的头指针。
int iCount; /*全局变量表示链表长度*/
struct Student *Creat()
{
struct Student *pHead=NULL; /*初始化链表,头指针为空*/
struct Student *pNew,*pEnd;
iCount=0; /*初始化链表长度*/
pEnd=pNew=(struct Student *)malloc(sizeof(struct Student);
printf("Please first enter name,then number\n");
scanf("%s",pNew->cName);
scanf("%d",pNew->iNumber);
while(pNew->iNumber!=0){
iCount++; //代表结点个数的增加
if(iCount==1){ //判断是否为第一次加入的结点
pNew->next=pHead; //指针指向为空
pEnd=pNew; //跟踪新加入的结点
pHead=pNew;
}
else
{
pNew->next=NULL;
pEnd->next=pNew;
pEnd=pNew;
}
pNew=(struct Student *)malloc(sizeof(struct Student); //为新结点再次分配内存空间
scanf("%s",pNew->cName);
scanf("%d",pNew->iNumber);
}
free(pNew); //释放结点空间
return pHead;
}
已有结点时,先将新结点pNew的指针指向NULL,然后将原来最后一个结点的指针指向新结点,最后将pEnd指针指向最后一个节点(新结点)。一个结点创建完后,要再次进行内存分配,并输入数据,通过while语句判断其是否符合要求,当结点不符合要求时,结束while语句通过free函数将不符合要求的节点空间去进行释放。
单链表的遍历
遍历链表中的数据并进行输出
void print (struct *pHead)
{
struct Student *pTemp; //循环所用的临时指针
int iIndex=1; //表示链表中的结点序号
printf("名单中共有%d个学生",iCount);
pTemp=pHead; //得到首节点的地址
while(pTemp!=NULL){
printf("第%d个学生是:\n",iIndex);
printf("%s\n",pTemp->cName); //输出信息
printf("%c\n",pTemp->iNumber);
pTemp=pTemp->next; //移动临时指针到下一个结点
iIndex++; //自加
}
}
在while循环中,每输出一个结点的内容,临时指针就移动到下一个结点,当为最后一个结点时,指针指向NULL,循环结束。
单链表的插入
在头结点位置进行插入
struct Student *Insert(struct Student *pHead)
{
struct Student *pNew; //定义pNew指向分配的空间
printf("请输入学生的姓名和学号:\n");
pNew=(struct Student *)malloc(sizeof(struct Student); //分配内存空间
scanf("%s",pNew->cName);
scanf("%c",pNew->iNumber);
pNew->next=pHead; //新结点指针指向原来的首结点
pHead=pNew; //头指针指向新结点
iCount++; //结点个数增加
return pHead; //返回头指针
}
首先为插入的新节点分配内存,然后输入数据。先将新节点的指针指向原来的首结点,保存首结点的地址,然后将头结点指向新结点,最后增加结点数量。
若需在结点(学号为number)后的位置插入,需要先找到学号为number的节点位置,然后为新插入的结点分配内存,向新结点输入数据。插入时,首先将新结点的指针指向插入结点的下一个结点,然后将插入结点的指针指向新结点,最后再增加结点数量,返回头指针。
struct Student *Insert(struct Student *pHead,int number)
{
struct Student *p=pHead,pNew;
while(p&&p->iNumber!=number) p=p->next;
printf("请输入学生的姓名和学号:\n");
pNew=(struct Student *)malloc(sizeof(struct Student *));
scanf("%s",pNew->cName);
scanf("%d",&->iNumber);
pNew->next=p->next; //新结点的指针指向插入结点的下一个结点
p->next=pNew; //插入结点的指针指向新结点
iCount++; //结点数量增加
return pHead;
}
删除
void Delete(struct Student *pHead,int iIndex) //iIndex为要删除的结点序号
{
int i;
struct Student *pTemp; //临时指针
struct Student *pPre; //表示要删除结点前的结点
pTemp=pHead; //得到头结点
pPre=pTemp;
printf("删除第&d个学生\n",iIndex);
for(i=0;i<iIndex;i++){ //使pTemp指向要删除的结点
pPre=pTemp;
pTemp=pTemp->next;
}
pPre->next=pTemp->next; //连接要删除的结点两边的结点
free(pTemp); //释放要删除节点的内存
iCount--; //结点数量减少
}
定义两个指针,分别表示要删除的结点和这个结点之前的结点,利用for循环来找到要删除的结点,pTemp保存要删除结点的地址, pPre保存前一个结点的地址,连接要删除结点两边的结点,使用free函数将pTemp指向的内存空间进行释放。
//头插法创建链表
struct Student *head,*p;
head=(struct Student *)malloc(sizeof(struct Student));
head->next=NULL;
int i; //要创建多少个结点
for(i=0;i<5;i++)
{
p=(struct Student *)malloc(sizeof(struct Student));
scanf("%d",&p->iNumber);
scanf("%s",p->cName);
p->next=head->next;
head->next=p;
}
//尾插法创建链表
struct Student *head,*p,*q;
q=(struct Student *)malloc(sizeof(struct Student));
q->next=NULL;
int i; //要创建多少个结点
for(i=0;i<5;i++)
{
p=(struct Student *)malloc(sizeof(struct Student));
scanf("%d",&p->iNumber);
scanf("%s",p->cName);
p->next=NULL;
q->next=p;
q=p;
}