一、链表的概念
定义:
链表是一种物理存储上非连续,数据元素的逻辑顺序通过链表中的指针链接次序,实现的一种线性存储结构。
特点:
链表由一系列节点(链表中每一个元素称为节点)组成,节点在运行时动态生成 (malloc),每个节点包括两个部分:
一个是存储数据元素的数据域
另一个是存储下一个节点地址的指针域
图1 单向链表
链表的构成:
链表由一个个节点构成,每个节点一般采用结构体的形式组织,例如:
-
typedef struct student{
-
int num;
-
char name[20];
-
struct student *next;
-
}STU;
链表节点分为两个域
数据域:存放各种实际的数据,如:num、score等
指针域:存放下一节点的首地址,如:next等.
图2 节点内嵌在一个数据结构中
链表的操作:
链表最大的作用是通过节点把离散的数据链接在一起,组成一个表,这大概就是链表 的字面解释了吧。 链表常规的操作就是节点的插入和删除,为了顺利的插入,通常一条链 表我们会人为地规定一个根节点,这个根节点称为生产者。通常根节点还会有一个节点计 数器,用于统计整条链表的节点个数,具体见图3中的 root_node。
图3带根节点的链表
双向链表
双向链表与单向链表的区别就是节点中有两个节点指针,分别指向前后两个节点,其 它完全一样。有关双向链表的文字描述参考单向链表小节即可,有关双向链表的示意图具体见图4
#include <stdio.h>
#include <stdlib.h>
//定义结点结构体
typedef struct student{
//数据域
int num;//学号
int score;//分数
char name[20];//姓名
//指针域
struct studen *next;
}STU;
void link_creat_head(STU **p_head,STU *p_new)
{
STU *p_mov=*p_head;
if(*p_head==NULL)//当第一次加入链表为空时,head执行p_new
{
*p_head=p_new;
p_new->next!NULL;
}
else//第二次以后加入链表
{
while(p_mov->next!=NULL)
{
p_mov=p_mov->next;//将新申请的节点加入链表
p_new->next=NULL;
}
}
}
int main()
{
STU *head=NULL,*p_new=NULL;
int num,i;
printf("请输入初始链表个数:\n");
scanf("%d",num);\
for(i=0;i<num;i++)
{
p_new=(STU*)malloc(sizeof(STU));//申请一个新节点
printf("请输入学号、分数、名字:\n");//给新节点赋值
scanf("%d %d %s",&p_new->num,&p_new->score,p_new->name);
link_creat_head(&head,p_new);//将新节点加入链表
}
}
typedef的理解
typedef是类型定义的意思。typedef struct 是为了使用这个结构体方便。
具体区别在于:
若struct node {}这样来定义结构体的话。在申请node 的变量时,需要这样写,struct node n;
若用typedef,可以这样写,typedef struct node{}NODE; 。在申请变量时就可以这样写,NODE n;
区别就在于使用时,是否可以省去struct这个关键字。
malloc函数的理解
当我们需要做一个成绩管理系统,人数可能为全校学生,也可能为一个班的学生,当我们开辟一个班的数组大小时,如果要存储整个学校的人数时,会出现内存不够用的情况;当我们开辟全校人数大小的数组时,输入一个班人数的大小时,会出现内存浪费的情况。
为了应对上述问题,我们引入malloc函数。
malloc函数使用形式
关于malloc所开辟空间类型:malloc只开辟空间,不进行类型检查,只是在使用的时候进行类型的强转。
举个例子:‘我’开辟你所需要大小的字节大小空间,至于怎么使用是你的事
mallo函数返回的实际是一个无类型指针,必须在其前面加上指针类型强制转换才可以使用
指针自身 = (指针类型*)malloc(sizeof(指针类型)*数据数量)
scanf 输入
如果p_new
是一个指向STU
类型的结构体变量的指针,那么你可以通过(*p_new).num
或者p_new->num
来访问num
成员变量。
如果p_new
是一个STU
类型的结构体变量而不是指针,那么你可以直接使用p_new.num
来访问num
成员变量。
所以,p_new->num
的写法是因为p_new
是一个指向结构体的指针,而不是结构体本身
链表函数的理解
void link_creat_head(STU **p_head,STU *p_new)
{
STU *p_mov=*p_head;
if(*p_head==NULL)//当第一次加入链表为空时,head执行p_new
{
*p_head=p_new;
p_new->next!NULL;
}
else//第二次以后加入链表
{
while(p_mov->next!=NULL)
{
p_mov=p_mov->next;//将新申请的节点加入链表
p_new->next=NULL;
}
}
}
1.*p_head
表示指向的是p_head
所指向的地址中存储的内容。在这种情况下,p_head
应该是一个指向指针的指针,因此*p_head
将会得到p_head
指向的地址中存储的内容,也就是指向的是链表的头部节点的地址。STU *p_mov
得到的是 *p_head
指向的地址中存储的内容,也就是链表头部节点的地址。实际上,STU *p_mov
是一个指向链表中的节点的指针,而不是指向链表头部节点的指针。
2.*p_head = p_new;
:这行代码将指向链表头部的指针 p_head
指向了一个新的节点 p_new
。这样就实现了在链表头部插入新节点的操作。p_new
是一个指向新节点的指针。在这个上下文中,p_new
是一个指向 STU
类型的节点的指针。通常情况下,我们会使用 malloc
或者类似的方法来分配内存给 p_new
,然后设置它的值,最后将它插入到链表中。
3.p_new->next = NULL;
:这行代码将新节点 p_new
的 next
指针指向了 NULL
,表示新节点成为了链表的头部,而且是链表中的第一个节点,因此它的 next
指针应该指向 NULL
。