在没有指针的时代,要描述静态链表是比较困难的,前人想到了使用数组与游标来表示单链表的方法,如下图

在静态链表中,数组第一位的游标存放第一位没有数据的元素的下标,数组最后一位的游标存放第一位有数据的元素的下标。
这种用数组描述的链表叫做静态链表,这种描述方法叫做游标实现法
1. 线性表的静态链表存储结构及初始化
#define MAXSIZE 1000
typedef struct {
ElemType data;//数据
int cur;//游标(Cursor)
}Component,StaticLinkList[MAXSIZE];
Status InitList(StaticLinkList space)
{
for (int i = 0; i < MAXSIZE-1; i++) {
space[i].cur = i+1;//初始化的时候除了最后一位,前面的游标都指向下一位
}
space[MAXSIZE-1].cur = 0;//最后一位游标指向第一位
return OK;
}
备忘:
— 我们对数组的第一位和最后一位元素做特殊处理,他们的data不存放数据
— 我们通常把未使用的数组元素称为备用链表
— 数组的第一个元素,即下标为0的那个元素的cur就存放备用链表的第一个结点的下标
— 数组的最后一个元素,即下标为MAXSIZE-1的cur则存放第一个有数值的元素的下标,相当于单链表中的头结点作用
2.获取静态链表的长度
ListLength
int ListLength(StaticLinkList L){
int sum = 0;
int i = L[MAXSIZE-1].cur;
while (i) {
sum++;
i = L[i].cur;
}
return sum;
}
3.静态链表的插入操作
静态链表中要实现元素的插入,需要解决的是:如何用静态模拟动态链表结构的存储空间分配,也就是需要的时候申请,不需要的时候释放
在动态链表中,结点的申请和释放分别借用C语言的malloc()和free()两个函数来实现
在静态链表中,操作的是数组,不存在像动态链表的结点申请和释放的问题,所以我们需要自己实现这两个函数,才可以做到插入和删除操作。
另外,我们需要辨明数组中哪些分量未被使用,解决的方法是讲所有未被使用过的及已被删除的分量用游标链成一个备用的链表,每当进行插入时,便可以从备用链表上取得第一个节点作为待插入的新结点,如图所示,我们要在A后面插入B:

插入操作需要自己实现Malloc()函数,即获取备选链表的开始结点下标
ListInsert:
//获取空闲分量的下标
int Malloc_SLL(StaticLinkList space)
{
int i = space[0].cur;
if (i != 0) {
//如果不是空链表,那么把备用链表的下一个分量作为备用链表的起始
space[0].cur = space[i].cur;
}
return i;
}
Status ListInsert(StaticLinkList L,int i,ElemType e){
int j,k,l;
k = MAXSIZE - 1; //数组的最后一个元素
if (i < 1 || i > ListLength(L)+1) {
return ERROR;
}
j = Malloc_SLL(L);
if (j) {
L[j].data = e;
//循环找到第i-1个元素的下标
for (l = 1; l < i; l++) {
k = L[k].cur;
}
//将第i-1的游标值赋值给L[j].cur
L[j].cur = L[k].cur;
//让第i-1个元素指向L[j]
L[k].cur = j;
return OK;
}
return ERROR;
}
4.静态链表的删除操作操作
删除如图示:

删除操作我们需要实现Free()函数,在调用Free()函数之前,将删除位置直接前驱结点的游标指向删除位置直接后继结点。
ListDelete
//释放结点
void Free_SLL(StaticLinkList space,int k){
//让此结点游标指向备用链表的开始结点
space[k].cur = space[0].cur;
//备用链表的头指针指向此结点
space[0].cur = k;
}
Status ListDelete(StaticLinkList L,int i,ElemType *e)
{
if (i < 1 || i > ListLength(L)) {
return ERROR;
}
//返回数据
*e = L[i].data;
//初始化为头结点下标
int k = MAXSIZE-1;
int j;
//循环找到i-1位置的下标
for (j = 1; j < i-1; j++) {
k = L[k].cur;
}
//临时变量存储i位置的下标
j = L[k].cur;
//将i-1位置的游标指向i+1位置的下标
L[k].cur = L[j].cur;
//释放i位置的结点
Free_SLL(L, j);
return OK;
}
5.静态链表优缺点总结
.优点
— 在插入和删除操作时,只需要修改游标,不需要移动元素,从而改进了在顺序存储结构中的插入和删除操作需要移动大量元素的缺点
.缺点
— 没有解决连续存储分配(数组)带来的表长难以确定的问题
— 失去了顺序存储结构随机存取的特性
.总的来说,静态链表其实是为了给没有指针的编程语言设计的一种实现单链表功能的方法
.尽管我们可以用单链表就不用静态链表了,但这样的思考方式是非常巧妙的,应该理解其思想,以备不时之需
网友评论