1.静态链表的概述
我们了解线性表两种存储结构各自的特点,那么,是否存在一种存储结构,可以融合顺序表和链表各自的优点,从而 既能快速访问元素,又能快速增加或删除数据元素。
使用静态链表存储数据,数据全部存储在数组中(和顺序表一样),但存储位置是随机的,数据之间"一对一"的逻辑关系通过一个整形变量(称为"游标",和指针功能类似)维持(和链表类似)。
数据存放到数组中时,给各个数据元素配备一个整形变量,此变量用于指明各个元素的直接后继元素所在数组中的位置下标
在某些高级语言中,没有指针类型,所以想使用链表,得靠其它手段,比如静态链表
静态链表是顺序表和链表的结合,在初始化时申请一定大小的空间(可以等同于定义一定长度的数组),数组的元素是一个结构体变量,结构体内有两个元素,一是数据,二是游标(相当于链表中的指针),游标保存的是下一个节点的下标
静态链表内部有两个链表,数据链表和备用链表
数据链表和备用链表各有一个头节点(链表没有头节点操作非常不方便),因此如果申请10个节点的空间,能保存数据的仅有8个节点
一般情况下,第一个节点,也就是下标为0的元素,作为备用节点的头节点,最后一个元素,也就是最大下标的元素,作为数据链表的头节点
typedef struct {
int data;//数据域
int cur;//游标
}component;
静态链表的初始化
其实上面显示的静态链表还不够完整,静态链表中,除了数据本身通过游标组成的链表外,还需要有一条连接各个空闲位置的链表,称为备用链表。
备用链表的作用是:
回收数组中未使用或之前使用过(目前未使用)的存储空间,留待后期使用。
也就是说,静态链表使用数组申请的物理空间中,存有两个链表,一条连接数据,另一条连接数组中未使用的空间。通常,备用链表的表头位于数组下标为 0(a[0]) 的位置,而数据链表的表头位于数组下标为 1(a[1])的位置。
静态链表中设置备用链表的好处是:
可以清楚地知道数组中是否有空闲位置,以便数据链表添加新数据时使用。比如,若静态链表中数组下标为 0 的位置上存有数据,则证明数组已满。
void InitArr(component *array) {
int i = 0;
for (i = 0; i < maxSize; i++) {
array[i].cur = i + 1;//将每个数组分量链接到一起
array[i].data = 0;
}
array[maxSize - 1].cur = 0;//链表最后一个结点的游标值为0
}
int mallocArr(component * array) {
//若备用链表非空,则返回分配的结点下标,否则返回 0(当分配最后一个结点时,
//该结点的游标值为 0)
int i = array[0].cur;
if (array[0].cur) {
array[0].cur = array[i].cur;
}
return i;
}
//向链表中插入数据,body表示链表的头结点在数组中的位置,add表示插入元素的位置,num表示要插入的数据
void insertArr(component * array, int body, int add, int num) {
int tempBody = body;//tempBody做遍历结构体数组使用
int i = 0, insert = 0;
//找到要插入位置的上一个结点在数组中的位置
for (i = 1; i < add; i++) {
tempBody = array[tempBody].cur;
}
insert = mallocArr(array);//申请空间,准备插入
array[insert].data = num;
array[insert].cur = array[tempBody].cur;//新插入结点的游标等于其直接前驱结点的游标
array[tempBody].cur = insert;//直接前驱结点的游标等于新插入结点所在数组中的下标
}
void freeArr(component * array, int k) {
array[k].cur = array[0].cur;
array[0].cur = k;
}
//删除结点函数,num表示被删除结点中数据域存放的数据
int deletArr(component * array, int num) {
int tempBody =maxSize-1;
int del = 0;
int newbody = 0;
//找到被删除结点的位置
while (array[tempBody].data != num) {
tempBody = array[tempBody].cur;
//当tempBody为0时,表示链表遍历结束,说明链表中没有存储该数据的结点
if (tempBody == 0) {
printf("链表中没有此数据");
return 0;
}
}
//运行到此,证明有该结点
del = tempBody;
tempBody = maxSize-1;
//删除首元结点,需要特殊考虑
if (del == array[maxSize-1].cur) {
newbody = array[del].cur;
array[maxSize-1].cur=newbody;
freeArr(array, del);
}
else
{
//找到该结点的上一个结点,做删除操作
while (array[tempBody].cur != del) {
tempBody = array[tempBody].cur;
}
//将被删除结点的游标直接给被删除结点的上一个结点
array[tempBody].cur = array[del].cur;
//回收被摘除节点的空间
freeArr(array, del);
}
}
void amendElem(component * array, int oldElem, int newElem) {
int add = selectNum(array,oldElem);
if (add == -1) {
printf("无更改元素");
return;
}
array[add].data = newElem;
}
int selectNum(component * array, int num) {
//当游标值为0时,表示链表结束
int tempBody = maxSize-1;
while (array[tempBody].cur != 0) {
if (array[tempBody].data == num) {
return tempBody;
}
tempBody = array[tempBody].cur;
}
//判断最后一个结点是否符合要求
if (array[tempBody].data == num) {
return tempBody;
}
return -1;//返回-1,表示在链表中没有找到该元素
}
以上就是基本函数定义