11.7链表
链表是一种最常见的数据结构,它动态地进行存储分配。
用指针处理链表
链表有单向链表、双向链表、环形链表等形式。
11.7.1链表概述
链表是将若干个数据项按一定的原则连接起来的表。
连接原则:
(1)前一个结点指向下一个结点
(2)只有通过前一个结点オ能找到下一个结点。
链表组成:
(1)头指针变量head一存放首元素地址,指向链表的首结点。
(2)每个结点由两个域组成:
数据域一一存储结点本身的信息。(用户有用信息)
指针域一一存放下一个结点的地址
(3)尾结点的指针域置为“NULL”,作为链表结束的标志。
链表结点数据可以用结构体来描述,例如
其中成员num和 score用来存放结点中的有用数据(用户
需要用到的数据),next是指针类型的成员,它指向 struct
student类型数据(这就是next所在的结构体类型)
struct student
{
long num;
float score;
struct student*next;/*指向下一结点*/
};
其中成员num和 score用来存放结点中的有用数据(用户
需要用到的数据),next是指针类型的成员,它指向 struct
student类型数据(这就是next所在的结构体类型)
11.7.2简单链表
静态链表:所有结点在程序中定义,没有动态申请空间,用完后也不能够释放空间
void main()
{
struct student a,b, c, head, *p; //对三个结点赋值
a.num=89101;a. score=89.5;
b. num=89103: 5. score=90
C num=89107: c score=85;
//建立彼此的链接,形成链表
head=&a;
a next=&b;
b. next=&c;
C next=NULL;
p=head;
//输出链表的数据信息
do
{
printf("1d5. 1f\n", p->num, p->score);
p=p->next;
}while(p!=NULL);
}
库函数< stdlib. h>和< alloc. h>提供动态地开辟和释放存储单元的有关函数
(1) malloc函数
函数原型:
void* malloc(unsigned int size)
功能:在内存的动态存储区分配size个字节的连续存储空间
返回值:
成功,返回一个指针,该指针指向存储空间首地址,基类型为void
失败,返回空指针(NULL)。原因:内存空间不够。
由于返回值是空类型指针,所以在调用时必须进行强制类型转换,将其转换成所需的类型。
如:
int *p=NULL;
p=(int*) malloc(sizeof(int));
(2)free函数
函数原型:
void free( void *p)
功能:
释放由p指向的空间。该地址p必须是最近一次由mllc、 calloc、 realloc函数申请成功返回的指针。
free函数无返回值,使用free前应检查该指针是否为空。
如:
int*p=NULL;
p=(int*)malloc(10* sizeof(int));
if(p!= NULL)
{
free(p);
p=NULL;
} //free和NULL配对使用,防止产生野指针
11.7.3线性链表
1.线性链表概述及其结构
线性表:当一组数据元素形成了“前后”关系时,我们称之为线性表
2.线性表在内存中的两种形式
顺序表:以数组形式存放,连续存放,插入或删除需要移动其他数据元素
线性链表:以指针形式链接数据单元,插入或删除不需要移动其他数据元素
struct 节点结构体类型名
{
数据成员定义;
struct 节点结构体类型名 *指针变量名;
};
2、线性链表的基本操作有:创建、插入、删除、输岀和销毁等。
链表的创建操作
含义:从无到有地建立起一个链表,即往空链表中依次插入若干结点,并保持结点之间的前驱和后继关系。
NODE *Create_LinkList()//创建链表
{
NODE *head,*tail,*pnew;
int scor;
head=(NODE*)malloc(sizeof(NODE));//创建头节点
if(head==NULL)//创建失败,则返回
{
printf("no enough memory!\n");
return( NULL);
}
head->next=NULL;//头节点的指针域置NULL
printf("input the score of students:\n");
while(1)//创建学生成绩线性链表
{
scanf("%d",&score);//输入成绩
if(score<0)//成绩为负,循环退出
break;
//创建一新节点
pnew=(NODE*)malloc(sizeof(NODE));
if(pnew==NULL)//创建新节点失败,则返回
{
printf("no enough memory!\n");
return(NULL);
}
pnew->score=score;
pnew->next=NULL;
tail->next=pnew;
tail=pnew;
}
return(head);
}
链表的插入操作
插入前,Ni是Ni+1的前驱,Ni+1是Ni的后继;插入后,新插入的结点N成为Ni的后继、Ni+1的前驱
void Insert_LinkList(NODE*head,NODE*pnew,int i)
{
NODE *p;
int j;
p=head;
for(j=0;j<i&&p!=NULL;j++)//将p指向要插入的第i个节点
p=p->next;
if(p==NULL)//表明链表中第i个节点不存在
{
printf("the %d node not foundt!\n",i);
return;
}
pnew->next=p->next;//将插入节点的指针域指向第i个节点的后继节点
p->next=pnew;//将第i个节点的指针域指向插入节点
}
链表的删除操作
删除前,节点Ni-1是Ni的前驱,Ni+1是Ni的后继;删除后,结点Ni+1成为Ni-1的后继。
记得释放删除节点的内存
void Delete_LinkList(NODE *head,int i)
{
NODE *p,*q;
int j;
if(i==0) return;//删除的是头指针
p=head;
//将p指向要删除的第i个节点的前驱节点
for(j=1;j<i&&p->next!=NULL;j++)
p=p->next;
if(p->next==NULL)//表明链表中第i个节点不存在
{
printf("the %d node not foundt!\n",i);
return;
}
q=p->next;//q指向待删除的节点i
p->next=q->next;//删除节点i
free(q);//释放节点i的内存单元
}
链表的输出操作
void Display_LinkList(NODE *head)
{
NODE *p;
for(p=head->next;p!=NULL;p=p->next)
printf("%d",p->score);
printf("\n");
}
链表的销毁操作
一个节点一个节点从后往前删除
void Free_LinkList(NODE *head)
{
NODE *p,*q;
p=head;
while(p->next!=NULL)
{
q=p->next;
p->next=q->next;
free(q);
}
}