在数据的存储中,有一种存储方式称为“线性表”,“线性表”是指数据具有“一对一”的逻辑方式,所有的数据像一根线一样,被串起来存储到物理空间中,“链表”属于“线性表”的一种,今天我们来学习下链表的使用。
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。
链表在插入的时候是O(1)的时间复杂度,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间复杂度,与另一种线性表——顺序表的插入O(n)的时间复杂度,查找O(1)的时间复杂度刚好相反。
1、某一元素的左侧相邻元素称为“直接前驱”,位于此元素左侧的所有元素都统称为“前驱元素”;
2、某一元素的右侧相邻元素称为“直接后继”,位于此元素右侧的所有元素都统称为“后继元素”;
3、链表中的每个存储元素,我们称之为“结点”,结点由数据域和指针域两部分组成,数据域存储数据,指针域存储下一个结点的地址,即指向直接后继元素。
结点内容:
链表形式:
链表有两种定义形式,一种是有头结点,一种没有头结点。
有头结点时,头指针指向头结点,头结点指向首元结点,首元结点再指向其他结点,以此类推。
无头结点时,头指针指向首元结点,首元结点指向其他结点,以此类推。
头指针,头结点和首元结点的概念:
1、头指针:它的特点是永远指向链表第一个节点的位置。很明显,头指针用于指明链表的位置,便于后期找到链表并使用表中的数据;
2、结点:链表中的又细分为头结点、首元结点和其他结点:
创建一个链表需要做如下工作:
1、声明一个头指针(如果有必要,可以声明一个头结点);
2、创建多个存储数据的结点,在创建的过程中,要随时与直接前驱建立逻辑关系。
以创建一个存储了数据{1,2,3,4}为例,代码如下(无头结点和有头结点):
#include "stdio.h"
typedef struct _link_list_t
{
int data;
struct _link_list_t *p_next;
}link_list_t;
void print_link_list_withoutHeaderPoint(link_list_t *temp)
{
while(temp)
{
printf("%d ",temp->data);
temp = temp->p_next;
}
}
void print_link_list_withHeaderPoint(link_list_t *temp)
{
while(temp->p_next)
{
temp = temp->p_next;
printf("%d ",temp->data);
}
}
//创建并初始化一个链表(含头结点),每个节点都有数据域和指针域,指针域指向下一个节点
link_list_t *LinkInitWithoutHeaderPoint(void)
{
int i;
link_list_t *header = NULL; //创建头指针,用来指向首元结点
link_list_t *temp = (link_list_t *)malloc(sizeof(link_list_t)); //创建一个首元结点
temp->data = 1;
temp->p_next = NULL; //首元结点初始化
header = temp; //头指针指向首元结点,与首元结点建立联系
for(i=2;i<5;i++)
{
link_list_t *a = (link_list_t *)malloc(sizeof(link_list_t));
a->data = i;
a->p_next = NULL; //节点初始化
temp->p_next = a; //temp结点指针域指向刚建立的节点,与其建立联系
temp = temp->p_next; //temp移动一位,以指向下一个节点
}
return header;
}
//创建并初始化一个链表(含头结点),每个节点都有数据域和指针域,指针域指向下一个节点
link_list_t *LinkInitWithHeaderPoint(void)
{
int i;
link_list_t *header = NULL; //创建头指针,用来指向头结点
link_list_t *temp = (link_list_t *)malloc(sizeof(link_list_t)); //创建一个头结点
header = temp; //头指针指向头结点
for(i=1;i<5;i++)
{
link_list_t *a = (link_list_t *)malloc(sizeof(link_list_t));
a->data = i;
a->p_next = NULL; //节点初始化
temp->p_next = a; //temp结点指针域指向刚建立的节点,与其建立联系
temp = temp->p_next; //temp移动一位,以指向下一个节点
}
return header;
}
int main(void)
{
//link_list_t *p = LinkInitWithoutHeaderPoint();
link_list_t *p = LinkInitWithHeaderPoint();
printf("创建并打印的链表数值为:\r\n");
//print_link_list_withoutHeaderPoint(p);
print_link_list_withHeaderPoint(p);
return 0;
}
1、在初始化链表时,结点需要动态分配内存malloc,每初始化新建一个结点,都需要和直接前驱建立联系,即将它的地址给到直接前驱的指针域。
2、头指针是结构体类型的指针,头结点数据域没有任何数据,指针域指向首元结点。
3、在打印有头结点的链表时,需要先执行temp = temp->p_next;再来打印,原因是temp指向的是头结点,而头结点是没有任何数据的。需要打印的是首元结点的数据,同理判断条件是while(temp->p_next),而不是while(temp).
如您在使用过程中有任何问题,请加QQ群进一步交流。
QQ交流群:906015840 (备注:物联网项目交流)