返回目录:
Chilan Yu:《数据结构》目录链接zhuanlan.zhihu.com2.3.5 静态链表
之前介绍的各种链表都是使用指针类型实现的,链表中结点空间的分配和回收都是由系统提供的标准函数malloc和free动态实现,所以称之为动态链表。
采用顺序存储结构数组模拟实现链表,在数组的每个表目中设置“游标cursor”来模拟指针,由程序员自己编写从数组中“分配结点”和“回收结点”的过程,这种方式被称为静态单链表。
游标实现链表的方法:定义一个较大的结构数组作为备用结点空间(即存储池)。
每个结点应含有两个域:data域和cursor域。
data域存放结点的数据信息,cursor域为游标指示器,指示后继结点在结构数组中的相对位置(即数组下标)。
数组的第0个分量可以设计成表的头结点,头结点的cursor域指示了表中第一个结点的位置,为0或-1表示静态单链表的结束。我们把这种用游标指示器实现的单链表叫做静态单链表(Static Linked List)。表尾结点的cursor域为0或-1,表示静态单链表的结束。(以下统一用0)
静态单链表可以借助结构体数组来描述:
#define ElemType int
#define Maxsize 11//静态链表的长度
typedef struct{
ElemType data;
int cursor;
}Component,StaticList[Maxsize];
通过变量定义语句StaticList S;
定义的静态单链表S中存储着线性表(a,b,c,d,f,g,h,i),Maxsize=11。
(1)在第四个元素后插入元素e:
先申请一个空闲空间并置入元素e,即令S[9].data = e
。
然后修改第四个元素的游标域,将e插入到链表中。即令S[9].cursor = S[4].cursor
,S[4].cursor = 9
。
(2)删除第八个元素h:
先顺着游标链通过计数找到第七个元素存储位置6。
然后删除的具体做法是令S[6].cursor = S[7].cursor
。
上述例子未考虑对已释放空间的回收,这样会造成静态链表的“假满”,即表中有很多的空闲空间,但却无法再插入元素。解决折耳根问题的方法是将所有未被分配的结点以及因删除操作而回收的结点空间通过游标链成一个空闲结点链表。
当进行插入操作时,先从空闲结点链表上取一个分量来存放待插入的元素,然后将其插入到已用链表的相应位置。
当进行删除操作时,则将被删除的结点空间链接到空闲结点链表上以备后用。
这种方法是指在已申请的大的存储空间中有一个已用的静态单链表,还有一个空闲结点链表。已用静态单链表的头指针为0,空闲结点静态单链表的头指针需另设一个变量av来存储。
1. 初始化
初始化就是将这个静态单链表初始化为一个空闲结点静态单链表,如图2.19所示。
设space为静态单链表存储空间的首地址,av为备用单链表的头指针。
/*静态单链表初始化*/
void initial(StaticList space, int * av){
space[0].cursor = 0;//设置已用静态单链表的头指针指向space空间位置0,space[0]相当于头结点
for(int k=1;k<Maxsize-1;++k)
space[k].cursor = k+1;//连链
space[Maxsize-1].cursor = 0;//标记链尾
*av = 1;//设置备用链表头指针初值
}
注意:已用空间头指针此时可以视为单链表的头结点,备用空间头指针av指向空闲结点静态链表中的第一个结点。
2. 分配结点空间
对于系统而言,在空闲结点链表中分配结点空间相当于空闲结点链表中减少(删除)一个结点,对使用者来说,相当于申请得到了一个可用的新结点。
/*分配结点*/
int getnode(StaticList space,int *av){//从备用链表摘下一个结点空间,分配给待插入静态链表中的元素
int i = *av;
*av = space[*av].cursor;
return i;//返回分配结点的数组下标
}
3. 回收结点空间
对系统而言,空闲结点链表回收空闲结点相当于空闲结点链表中增加一个结点,对使用者而言,相当于释放了一个不用的结点。
/*空闲结点回收*/
void freenode(StaticList space,int *av,int k){//从space备用链表中回收序号为k的结点,av为备用链表的头指针
space[k].cursor = *av;
*av = k;
}
4. 静态链表前插操作
【算法描述】
(1)先从备用单链表上取一个可用的结点。
(2)将其插入到已用静态单链表第i个元素之前。
/*前插操作*/
void insbefore(StaticList space,int i,int *av,ElemType x){//从备用单链表上取一个可用的结点,将其插入到已用静态单链表第i个元素之前
int k = getnode(space,av);//k为从备用表中取到的可用结点空间的下标
space[k].data = x;
int j = 0;//j为已用静态单链表的头结点的下标
for(int m=1;m<i;++m)//寻找第i -1个元素的位置j
j = space[j].cursor;
space[k].cursor = space[j].cursor;//修改游标域
space[j].cursor = k;
}
5. 静态链表删除
【算法描述】
(1)首先寻找第i -1个元素的位置,之后通过修改相应的游标域进行删除;(2)在将被删除的结点空间链到可用静态单链表中,实现回收。
/*删除操作*/
void Delete(StaticList space,int i,int *av){
int j = 0;//j为已用静态单链表的头结点的下标
for(int m=1;m<i;++m)//寻找第i-1个元素的位置j
j = space[j].cursor;
int k = space[j].cursor ;
space[j].cursor = space[k].cursor;//从删除第i个元素
freenode(space,av,k);//回收第i个空闲结点
}
以下附上静态链表的完整代码:
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
#define ElemType int
#define Maxsize 11//静态链表的长度
typedef struct{
ElemType data;
int cursor;
}Component,StaticList[Maxsize];
/*静态单链表初始化*/
void initial(StaticList space, int * av){
space[0].cursor = 0;//设置已用静态单链表的头指针指向space空间位置0,space[0]相当于头结点
space[0].data = 0;
for(int k=1;k<Maxsize-1;++k){
space[k].cursor = k+1;//连链
space[k].data = 0;
}
space[Maxsize-1].cursor = 0;//标记链尾
space[Maxsize-1].data = 0;
*av = 1;//设置备用链表头指针初值
}
/*分配结点*/
int getnode(StaticList space,int *av){//从备用链表摘下一个结点空间,分配给待插入静态链表中的元素
int i = *av;
*av = space[*av].cursor;//修改备用表的头指针
return i;//返回分配结点的数组下标
}
/*空闲结点回收*/
void freenode(StaticList space,int *av,int k){//从space备用链表中回收序号为k的结点,av为备用链表的头指针
space[k].cursor = *av;
*av = k;
}
/*前插操作*/
void insbefore(StaticList space,int i,int *av,ElemType x){//从备用单链表上取一个可用的结点,将其插入到已用静态单链表第i个元素之前
int k = getnode(space,av);//k为从备用表中取到的可用结点空间的下标
space[k].data = x;
int j = 0;//j为已用静态单链表的头结点的下标
for(int m=1;m<i;++m)//寻找第i -1个元素的位置j
j = space[j].cursor;
space[k].cursor = space[j].cursor;//修改游标域
space[j].cursor = k;
}
/*删除操作*/
void Delete(StaticList space,int i,int *av){
int j = 0;//j为已用静态单链表的头结点的下标
for(int m=1;m<i;++m)//寻找第i-1个元素的位置j
j = space[j].cursor;
int k = space[j].cursor ;
space[j].cursor = space[k].cursor;//从删除第i个元素
freenode(space,av,k);//回收第i个空闲结点
}
void show(StaticList space){
cout << " " << "data " << "cursor" << endl;
for(int i=0;i<Maxsize;++i){
cout << i << ":" << space[i].data << " " << space[i].cursor << endl;
}
cout << endl << "当前静态链表:";
for(int i=0;space[i].cursor!=0;i=space[i].cursor){//遍历输出静态链表
cout << space[space[i].cursor].data << " ";
}
cout << endl;
}
int main()
{
StaticList space;
int av;
initial(space,&av);
show(space);
int choice;
while(1){
cout << endl;
cout << "(1)插入元素 (2)删除元素 (3)退出" << endl;
cin >> choice;
if(choice==1){//插入操作
ElemType tmp1;
int loc1;
cout << "请输入待插入的元素:";
cin >> tmp1;
cout << "请输入待插入的位置:";
cin >> loc1;
insbefore(space,loc1,&av,tmp1);
show(space);
}
else if(choice==2){//删除操作
int loc2;
cout << "请输入待删除元素的位置:";
cin >> loc2;
Delete(space,loc2,&av);
show(space);
}
else{
return 0;
}
}
return 0;
}