静态链表
静态链表可以融合顺序表和链表各自的优点,从而既能快速访问元素,又能快速增加或删除数据元素。静态链表,也是线性存储结构的一种,它兼顾了顺序表和链表的优点于一身,可以看做是顺序表和链表的升级版。
使用静态链表存储数据,数据全部存储在数组中(和顺序表一样),但存储位置是随机的,数据之间"一对一"的逻辑关系通过一个整形变量(称为"游标",和指针功能类似)维持(和链表类似)。
静态链表的结构
节点结构
静态链表通过 “数组+游标” 的方式存储具有线性关系数据,例如
从上图看出静态链表存储数据元素也需要自定义数据类型,至少需要包含以下 2 部分信息:
- 数据域:用于存储数据元素的值;
- 游标:其实就是数组下标,表示直接后继元素所在数组中的位置;
相应的c语言数据结构描述为:
typedef struct Node{
int data; //数据域
int cur; //游标
}staticList;
存储示例
- 数组长度为7
- 能存储值的为7-1=6个空间,除了a[0]
- 包括备用表和数据表。数据表存储数据,备用表存储空闲节点
静态链表的初始化
在数据链表未初始化之前,数组中所有位置都处于空闲状态,因此都应被链接在备用链表上,当向静态链表中添加数据时,需提前从备用链表中摘除节点,以供新数据使用。
备用链表摘除节点最简单的方法是摘除 a[0] 的直接后继节点;同样,向备用链表中添加空闲节点也是添加作为 a[0] 新的直接后继节点。因为 a[0] 是备用链表的第一个节点,我们知道它的位置,操作它的直接后继节点相对容易,无需遍历备用链表,耗费的时间复杂度为 O(1)。
创建静态链表的c代码:
//从备用链表摘下一个结点分配空间
int mallocList(staticList *array){
//找到备用链表的第一个结点,这里是array[0]的下一结点
int i = array[0].cur;
//总是从头部拿走array[0]的下一结点结点
if(array[0].cur)
array[0].cur = array[i].cur;
//返回申请结点
if(i == 0){
printf("没有多于的结点可以申请\n");
exit(1);
}
return i;
}
//创建备用链表
void createArray(staticList *array){
int i;
int lencur = maxSize - 1;
//把备用链表串起来,直到maxsize-2
for(i = 0; i < lencur; ++i)
array[i].cur = i + 1;
//masize - 1的下一个是表头。
array[maxSize - 1].cur = 0;
}
//初始化静态链表
int initArray(staticList *array){
createArray(array);
//分配空间
int head = mallocList(array);
//声明一个变量当作指针来用
int ptr = head;
array[ptr].data = 1; //数据域为1
int i = 0;
for(i = 1; i < maxSize - 4; ++i){
int temp = mallocList(array); //从备用链表拿出来空闲结点
array[ptr].cur = temp; //将空闲结点放在链表的最后一个结点的后面
array[temp].data = i+1; //空闲结点赋值
ptr = temp; //调整指针指向最后一个结点
}
array[ptr].cur = 0; //最后结点的下一结点设置为0
return head; //返回链表头
}
静态链表的其他操作
静态链表插入数据
假如将元素添加到静态链表中的第 3 个位置上,实现过程如下:
- 从备用链表中摘除一个节点,用于存储元素 4;
- 找到表中第 2 个节点(添加位置的前一个节点,这里是数据元素 2),将元素 2 的游标赋值给新元素 4;
- 将元素 4 所在数组中的下标赋值给元素 2 的游标;
- 原先的下标指向插入下标
由于mallocList中已经判断备用链表有没有空闲节点了,这里不再判断。
//插入数据
void insert(staticList *array, int head, int pos, int elem){
int ptr = head;