0、为何学习链表
学习数据结构做准备。学了数据结构会发现数据可以组合成各种形状,线性表,堆栈,队列,串,树状,图等。
1、链表和数组的区别
数组建立之后不可修改(增删),链表可以。比如建立数组之后,增加一个元素,实现起来就是插入一个元素,然后该元素后面的所有元素都需要往后撤一个位置,数组实现起来很麻烦。再比如删掉一个元素,那么后面的元素都需要往前前进一个位置,实现起来也很麻烦。而链表实现增删操作则很容易,只是一句赋值语句而已。
数组是静态的,链表是动态的。虽然C99允许数组个数是一个变量,后期输入,但是一旦输入了也不好再修改,但是链表不同,链表非常方便完成增删操作,所以链表可以随时根据我们的需要增加或者释放一个元素。
2、链表的结构
链表有头指针和结点组成,头指针是一个存储单元,这个存储单元存放一个地址,该地址指向一个元素(结点),每个结点包括两部分,一部分是实际数据,另一部分是下一个结点的地址。链表类似火车,头指针像一个火车头,结点类似火车厢,火车厢通过每节车厢后的挂绳链接再一起,但是火车厢总有结束的时候,最后一节火车厢不链接任何东西,称为火车尾,类似的,链表中最后一个结点称为表尾,它也是一个结点,结点地址部分放一个NULL(空地址)。
3、静态链表的建立
链表有一个头指针和多个结点组成,头指针使用指针实现即可,而结点又包括两部分内容,且两部分内容的数据类型不一样,在我们所学习的数据类型中,只有结构可以胜任了,例如可以如下定义结点的结构类型:
struct Student
{
int num;
float score;
struct Student *next;
};
其中num和score是实际数据,next是下一个结点的地址,每个结点都是一个结构变量,所以指向结构变量的指针自然是结构类型。上述程序只是结点的类型,并不是实际的结点,或者说实际分配存储空间,只有定义了结构变量才会分配存储空间,例如下面的程序则为建立真实的链表:
#include <stdio.h>
struct Student{ //定义结构类型 //全局变量,main和子函数是处于同一个等级
int num;
float score;
struct Student *next;
};
int main(){
/*(1)定义各结点*/
struct Student a,b,c,*head;
/*(2)初始化各结点的实际数据*/
a.num =10101;a.score=88.5;
b.num =10103;b.score=90;
c.num =10107;c.score=85;
/*(3)将各结点链接在一起*/
head=&a;
a.next=&b;
b.next =&c;
c.next =NULL;
/*打印各结点的实际数据*/
struct Student *p=head; //1)定义指针指向头指针,头指针不可变,所以不可以使用头指针遍历整个链表
for(;p!=NULL;p=p->next){ //2)p=p->next实现p指向每一个结点 //关于for循环的大括号
printf("%ld %5.1f
",p->num,p->score); //3)使用->运算符打印输出每个结点数据
}
return 0; //关于返回值
}
建立链表需要上述(1)-(3)3个步骤,打印/输出链表则需要1)-3)3个步骤。
课上练习题:
例1:
(1)建立链表,实际数据包括学号,姓名。并使用同一排3名同学的信息进行初始化。
(2)输出3名同学的信息。
#include <stdio.h>
#include <string.h>
struct Student{
int num;
char name[20];
struct Student *next;
};
int main(){
struct Student a,b,c,*head;
a.num =22;strcpy(a.name,"于金池");
b.num =23;strcpy(b.name,"于港岩");
c.num =24;strcpy(c.name,"王丫");
head=&a;
a.next=&b;
b.next =&c;
c.next =NULL;
struct Student *p=head;
for(;p!=NULL;p=p->next){
printf("%ld %s
",p->num,p->name);
}
return 0;
}
例2:
(1)建立链表,实际数据包括学号,姓名。并使用同一排3名同学的信息进行初始化。
(2)建立函数printinfo,参数是结构指针,在该函数中实现输出3名同学的信息。
#include <stdio.h>
#include <string.h>
struct Student{
int num;
char name[20];
struct Student *next;
};
void printinfo(struct Student *p){
for(;p!=NULL;p=p->next){
printf("%ld %s
",p->num,p->name);
}
}
int main(){
struct Student a,b,c,*head;
a.num =22;strcpy(a.name,"于金池");
b.num =23;strcpy(b.name,"于港岩");
c.num =24;strcpy(c.name,"王丫");
head=&a;
a.next=&b;
b.next =&c;
c.next =NULL;
printinfo(head);
return 0;
}
例3:
(1)建立链表,实际数据包括学号,姓名。并使用同一排3名同学的信息进行初始化。
(2)建立函数searchinfo,参数是结构指针,在该函数中实现输出你自己的信息。
#include <stdio.h>
#include <string.h>
struct Student{
int num;
char name[20];
struct Student *next;
};
void searchinfo(struct Student *p){
for(;p!=NULL;p=p->next){
if(p->num ==22)
printf("%ld %s
",p->num,p->name);
}
}
int main(){
struct Student a,b,c,*head;
a.num =22;strcpy(a.name,"于金池");
b.num =23;strcpy(b.name,"于港岩");
c.num =24;strcpy(c.name,"王丫");
head=&a;
a.next=&b;
b.next =&c;
c.next =NULL;
searchinfo(head);
return 0;
}
在上述函数调用中,函数参数是链表头指针,因为通过链表头指针可以访问到链表中的任意结点,因此在定义相关的操作函数时,可以使用头指针作为参数和返回值。