文章目录
简介
作为另一种线性表的存储方式,链式存储的线性表(简称:链表)在存储方式上十分灵活,它不像顺序表一样需要一次开辟一个足够大的连续空间,而是可以利用零碎的内存空间来组成链表。链表的每个元素节点分为数据域和指针域,如图所示,数据域用来存储数据,指针域用来指向下一个元素节点。
多个数据节点通过指针连接到一起就可以构成链表,如图所示。
采用这种存储方式所构成的链表,在结构上和结构上都十分灵活,链表的长度可以动态地增加和减少,元素节点可以分配在零散的存储区间上,大大提高内存利用率。
当然链表也是一种线性表,所以其API函数的上使用,方式与线性表并无太大差异,只是实现方式有所不同。上图中的链表是最基本的链表结构,在实际使用过程中可以根据自己的需求进行调整和丰富。本文介绍的是带有表头的链表,加有表头的主要目的是便于对链表的长度进行管理,如图所示。
下面进行链表数据结构和API函数及其实现方式的介绍。
链表数据结构
c
//定义链表数据类型
typedef struct
{
int data;
LinkNode *next;
}LinkNode;
//定义链表数据类型
typedef struct
{
int length;
LinkNode head;
}List;
LinkNode是链表节点,每个节点包含数据data和指针域next;List是链表表头,包含链表长度length和链表元素头节点。
接口函数
链表生成函数
c
List* ListCreate()
{
//新建一个表头的指针,并将分配空间
List *temp = (List *)malloc(sizeof(List));
//将链表表头的后继设置为NULL,数据设置0(此参数无用)
temp->head.next = NULL;
temp->head.data = 0;
//将链表的长度,设置为0
temp->length = 0;
//返回
return temp;
}
在生成一个链表时,首先分配块List大小的内存空间,然后head头结点中的指针域指向NULL;数据域没有使用(创建时清0);链表长度设置为0。
应用代码:
c
List * MyList = NULL;
//生成一个线性表
MyList = ListCreate();
销毁函数:
c
int ListDestory(List **list)
{
int i;
if (*list == NULL)
{
//如果链表是否已经销毁,返回-1
return -1;
}
//依次删除各个元素
for (i = 0; i < (*list)->length; i++)
{
ListDelete((*list), i,NULL);
}
//释放链表内存空间
free(*list);
//将链表指针指向NULL
*list = NULL;
return 0;
}
链表销毁函数在销毁链表前需要先将链表中的元素节点删除掉,因为链表中的节点是在堆区分配的,因此在不使用时需要释放内存,避免内存的浪费;所有元素删除掉之后,释放表头的内存空间,将表头指针指向NULL。
应用代码:
c
ListDestory(&MyList);
清空函数
c
int ListClear(List *list)
{
int i;
if (list == NULL)
{
//如果链表是否已经销毁,返回-1
return -1;
}
//依次删除各个元素
for (i = 0; i < list->length; i++)
{
ListDelete(list, i, NULL);
}
//将链表长度设置为0
list->length = 0;
return 0;
}
清空函数与删除类似,但是不释放表头空间。首先删除链表中的元素,然后将链表长度清0,链表依然存在,只是里面没有了元素节点。
应用代码:
c
ListClear(MyList);
获取长度函数:
c
int ListGetLength(List *list)
{
if (list == NULL)
{
//如果输入参数为NULL,返回-1
return -1;
}
//返回链表长度
return list->length;
}
应用代码:
c
listLen = ListGetLength(MyList);
元素获取代码:
c
int ListGet(List *list, int position, int *e)
{
int i;
LinkNode *current = NULL;
LinkNode *data = NULL;
if (list == NULL || position < 0|| position > list->length)
{
//如果list为NULL,position超出链表长度,返回-1
return -1;
}
//将current指向表头的next指针指向的位置,即第一个元素的位置
current = &list->head;
for (i = 0; i < position; i++)
{
//指针后移一位
current = current->next;
}
//将节点中数据域中的值赋值给e
data = current->next;
*e = data->data;
return 0;
}
元素获取函数的作用是获取相应位置的元素。元素节点指针currnet用于表示当前位置元素,首先将他指向链表头节点(current = &list->head),执行后如4所示。从图中开看出,此时current->next就是第一个元素的,此后current每后移一次,current->next指向的元素将后移一位。根据position的值可以确定后移的次数,从可找到相应位置的元素。例如,要获取0位置的元素节点的数据值,此时current不需要后移,current->next的指向就是0位置元素节点。
应用代码:
c
ListGet(MyList,i,&temp);
元素插入函数:
c
int ListAdd(List *list, int position, int e)
{
int i;
//用于保存当前元素位置
LinkNode *current = NULL;
//用于元素插入
LinkNode *insert = NULL;
//判断输入参数是否有误
if (list == NULL || position < 0)
{
//如果list为NULL,position小于0,返回-1
return -1;
}
//在内存中分配一个元素节点
insert = (LinkNode *)malloc(sizeof(LinkNode));
//将要插入元素值,赋值给节点
insert->data = e;
//将节点后继指针赋值为NULL
insert->next = NULL;
//如果插入位置在超出末尾的位置,在末尾加入元素
if (position > list->length)
{
position = list->length;
}
//将current指向表头的next指针指向的位置,即第一个元素的位置
current = &list->head;
//将从position开始位置的元素向后移动一个位置
for (i = 0; i < position; i++)
{
current = current->next;
}
//在插入位置插入元素
insert->next = current->next;
current->next = insert;
//顺序表长度加1
list->length++;
return 0;
}
元素插入函数的作用是在相应位置数据。链表的插入和删除相对于顺序表而言要简单和灵活很多,链表元素的插入如图5所示。插入前如图5左侧所示,此时data1节点的next值指向data2节点。若要在data1和data2之间插入元素data,只需要先将data的next指向data2,然后将data1的next指向data,即可实现元素的插入。
在删除相应位置元素的时候,首先需要将current的指向移动相应的位置,然后按照元素插入的步骤插入元素即可。
应用代码:
c
ListAdd(MyList,0,data0);
ListAdd(MyList,1,data1);
ListAdd(MyList,2,data2);
元素删除函数:
c
int ListDelete(List *list, int position, int *e)
{
int i;
LinkNode *current = NULL;
LinkNode *dele = NULL;
//判断输入参数是否有误
if (list == NULL || position < 0)
{
//如果list为NULL,position小于0或者超出链表长度,返回-1
return -1;
}
//如果删除位置在超出末尾的位置,在末尾删除元素
if (position > list->length)
{
position = list->length;
}
//将current指向表头的next指针指向的位置,即第一个元素的位置
current = &list->head;
//将从position开始位置的元素向后移动一个位置
for (i = 0; i < position; i++)
{
current = current->next;
}
//删除相应位置的元素
dele = current->next;
//保存要删除位置的元素
if (e != NULL)
{
*e = dele->data;
}
current->next = dele->next;
//释放内存空间,将指针指向NULL
free(dele);
dele = NULL;
//顺序表长度减1
list->length--;
return 0;
}
元素删除函数的作用是删除相应位置的元素节点,其删除方式与插入一样都是通过改变指针的指向来进行的。链表元素删除如图6所示,当要删除data2对应的节点时,只要将data1的next指针指向data3即可。值得注意的是,删除的节点的内存空间必须释放掉,不然会造成内存泄漏。将指针移动到不同的位置然后根据删除步骤就能删除链表中相应位置的元素。
应用代码:
c
ListDelete(MyList, 0, &temp);
printf("dele:%d\n",temp);