链表简介:链表是一种常见的数据结构。 数组可以包含数据,但使用数组时,请指定数组中包含的元素数,即数组的长度。 但是,如果添加到此数组的元素数超过了数组的大小,则无法完全保存内容。 例如,在定义班级人数时,如果小班有30人,普通班有50人,并且在定义班级人数时使用数组,则必须定义最大数组数,即最少50个元素。 否则,就满足不了最大的情况。 这种方式非常浪费空间。 链表对存储元素的数量没有限制,添加元素后存储的数量会发生变化。
图1显示了链表结构的示意图
链表中有一个头指针变量,图中的head表示头指针,该指针变量保存地址。 从图中的箭头可以看到,这个地址是变量的地址。 也就是说,头指针指向变量。 此变量称为元素,链表中的每个元素都有两个部分:数据部分和指针部分。 数据部分保存元素中包含的数据,指针部分用于指向以下元素: 最后一个元素指针指向NULL,指示指向的地址为空。 从图1中可以看到,头部节点指向第一个元素,第一个元素的指针指向第二个元素,第二个元素的指针指向第三个元素的地址,第三个元素的指针指向空。
根据链表的说明,可以想象链表就像一条链子,一条条地连接在一起。 然后,用头部指针查找链表的元素。 这是幼儿园,老师牵着第一个孩子的手,第一个孩子又牵着第二个孩子的手,就像幼儿园的孩子们连成一条线。 最后一个孩子谁也没拉。 他的手空着。 他好像是链表链条的最后一个。 老师是头指针。 通过老师你可以找到这个团队的任何孩子。
注意:像链表这样的结构,必须利用指针才能实现。 因此,链表中的节点必须包含保存以下节点地址的指针变量:
三个函数:
统一集成(void * malloc );
此函数的功能是动态分配大小为size的内存空间给内存。 malloc函数返回指向分配的内存空间的指针,如果发生错误,则返回NULL。
void*calloc(unsignedintn,unsigned int size );
此函数的功能是动态分配长度为size的n个连续内存空间数组给内存。 calloc函数返回指向动态分配的连续内存空间地址的指针。 如果分配空间错误,则返回NULL。
语音自由(语音* Ptr );
此函数的功能是使用指针ptr指向的内存空间,使某些内存空间可供其他变量使用(简单来说,就是释放不需要的内存空间)。 ptr是上次调用calloc或malloc函数时返回的值,free函数没有返回值。
链表插入操作:链表的插入操作可以在链表开头的指针位置进行(图3 ),也可以在某个节点的位置进行,或者也可以像创建结构时那样在链表后面添加节点(图2 ) 这三个插入操作的思想都是一样的。
在链表头插入节点的过程,就像牵着手的孩子们连成一条线。 那时,又来了一个孩子,他将站在老师和孩子们之间。 那么老师必须放开原来的孩子们,留住新加入的孩子们。 这个新加入的孩子会留住原来的孩子。 这样,这条相连的线还是相连的。
链表删除操作:删除链表节点该怎么办? 果然还是通过前言中的孩子们牵手的比喻来理解。 例如,队伍中的一个孩子离开了队伍。 而且,这个队伍打不完的方法,只要两边的孩子举手就行了。
例如,删除其中一个链表。
从图4可以看出,删除一个节点首先需要找到该节点的位置,例如图中的NO2节点。 然后,将NO1节点的指针指向NO3节点,最后释放NO2节点的存储器空间,由此完成节点的删除操作。
实例:
#包含
#包含
结构化//学生结构
{
char name[20]; //名称
Int编号器; //学号
结构稳定*下一步; //指向下一个节点的指针
(;
输入计数; //全局变量表示链表的长度
/*
create函数的功能是创建链表,在create外部显示整数全局变量count。 这个变量是
的作用是表示链表中的节点数。 create函数首先定义要使用的指针变量,而head用于表示标头
指针,end指向原始末尾的节点,new指向新创建的节点。
使用malloc函数分配内存。 首先,用end和new指针指向第一个分配的内存。 然后显示提示消息,
首先输入学生的名字,然后输入学生的学校号码。 使用while进行判断,如果学校编号为0,则不执行循环语句。
在while循环中,计数自加操作表示链表中节点的增加。 然后判断新加入的节点是否是第一个
加入其他节点,首次加入节点时执行if语句
块中的代码,否则执行else语句块中的代码。在if语句块中,因为第一次加入结点时其中没有结点,所以新结点即为首结点也为最后一个结点,
并且要将新加入的结点的指针指向NULL,即为head指向。else语句实现的是链表中已经有结点存在时的
操作。首先将新结点new的指针指向NULL,然后将原来最后一个结点的指针指向新结点,最后将end指针
指向最后一个结点。
这样一个结点创建完之后,要再进行分配内存,然后向其中输入数据,通过while语句再次判断输入
的数据是否符合结点的要求。当结点不符合要求时,执行下面的代码,调用free函数将不符合要求的结点
空间进行释放。
*/
struct student * create()
{
struct student * head = NULL;//初始化链表头指针为空
struct student * end,* new;
count = 0;//初始化链表长度
end = new = (struct student *)malloc(sizeof(struct student));
printf("please first enter name,then nembern");
scanf("%s",new->name);
scanf("%d",&new->number);
while(0 != new->number)
{
count++;
if(1 == count)
{
new->next = head;//使得指向为空
end = new;//跟踪新加入的结点
head = new;//头指针指向首结点
}
else
{
new->next = NULL;//新结点的指针为空
end->next = new;//原来的尾结点指向新结点
end = new;//end指向新结点
}
new = (struct student *)malloc(sizeof(struct student));//再次分配结点内存空间
scanf("%s",new->name);
scanf("%d",&new->number);
}
free(new);//释放没用到的空间
return head;
}
/*
print函数是用来将链表中的数据进行输出。在函数的参数中,head表示一个链表的头结点。
在函数中,定义一个临时的指针temp用来进行循环操作。定义一个整型变量表示链表中的结点序号。
然后将临时指针temp指针变量保存首结点的地址。
使用while语句将所有的结点中保存的数据都显示输出。其中每输出一个结点的内容后,就移动
temp指针变量指向下一个结点的地址。当最后一个结点时,所拥有的指针指向NULL,此时循环结束。
*/
void print(struct student * head)
{
struct student *temp;//循环所用的临时指针
int index = 1;//表示链表中结点的序号
printf("---the list has %d members:---nn",count);
temp = head;//指针得到首结点的地址
while(NULL != temp)
{
printf("the NO%d member is:n",index); //输出结点序号
printf("the name is:%sn",temp->name);//输出姓名
printf("the number is:%dnn",temp->number); //输出学号
temp = temp->next;//移动临时指针到下一个结点
index++;
}
}
/*
insert函数为链表头插入操作函数,在代码中,为要插入的新结点分配内存,然后向新结点中输入数据。这样
一个结点就创建完成了,接下来就是将这个结果插入到链表中。首先将新结点的指针指向原来的首结点,保存首结
点的地址。然后将头指针指向新结点,这样就完成了结点的连接操作,最后增加链表的结点数量。
*/
struct student * insert(struct student * head)
{
struct student * new;//指向新分配的空间
printf("---insert member at first---n");
new = (struct student *)malloc(sizeof(struct student));//分配内存空间,并返回指向该内存空间的指针
scanf("%s",new->name);
scanf("%d",&new->number);
new->next = head;//新结点指针指向原来的首结点
head = new;//头指针指向新结点
count++;//增加链表结点数量
return head;
}
/*
为delete函数传递两个参数,head表示链表的头指针,index表示要删除结点在链表中的位置。
定义整型变量i用来控制循环的次数,然后定义两个指针,分别用来表示要删除的结点和这个结点
之前的结点。
输出一行提示信息表示要进行删除操作,之后利用for语句进行循环操作找到要删除的结点,
使用temp保存要删除结点的地址,pre保存前一个结点的地址。找到要删除的结点后,连接删除结点
两边的结点,并使用free函数将temp指向的内存空间进行释放。
*/
void delete(struct student * head,int index)//head表示头结点,index表示要删除的结点下标
{
int i;//控制循环变量
struct student * temp;//临时指针
struct student * pre;//表示要删除结点前的结点
temp = head;//得到头结点
pre = temp;
printf("---delete NO%d member---nnn",index);
for(i=1;i
{
pre = temp;
temp = temp->next;
}
pre->next = temp->next;//连接删除结点两边的结点
free(temp);//释放掉要删除结点的内存空间
count--;//减少链表中的元素个数
}
/*
在main函数中,先定义一个头结点指针head,然后调用create函数创建链表,并将链表的头结点
返回给head指针变量。利用得到的头结点head作为print函数的参数。
在main函数中分别添加代码执行插入和删除操作。
*/
int main()
{
struct student * head;//定义头结点
head = create();//创建结点
print(head);//输出链表
head = insert(head);//插入结点
print(head);//输出链表
delete(head,2);//删除第二个结点
print(head);//输出链表
return 0;
}
参考文献:[C语言从入门到精通].故意的身影等
[C语言从入门到精通].故意的身影等PDF文档下载地址: