1、概念
静态链表,也是线性存储结构的一种,它兼顾了顺序表和链表的优点于一身,可以看做是顺序表和链表的升级版。使用静态链表存储数据,数据全部存储在数组中(和顺序表一样),但存储位置是随机的,数据之间"一对一"的逻辑关系通过一个整形变量(称为"游标",和指针功能类似)维持(和链表类似)。
2、存储结构
struct Component{
ElemType data;
int cur; //代表游标,为0时无指向
};
typedef Component StaticLinkList[MAXSIZE]; //结构体数组
3、初始化
数组中的第一个元素和最后一个元素作为特殊元素处理,不存放数据。通常把数组中未存放数据的元素称为备用链表。而数组的第一个元素,即下表为0的元素的cur存放备用链表的第一个结点的下标;数组中最后一个元素,即下标为(MAXSIZE - 1)的元素的cur存放第一个有数值的元素的下标,相当于单链表中头结点的作用。
/*初始化数组线性表,也是将数组链接成备用链表,其中space[0].cur代表头指针*/
Status InitList(StaticLinkList &space)
{
for (int i = 0; i < MAXSIZE - 1; i++)
space[i].cur = i + 1;
space[MAXSIZE - 1].cur = 0; //目前静态链表为空,所以最后一元素的cur=0
return OK;
}
4、静态链表插入操作
1)插入前我们需要考虑,由于要多存入一个元素,那么就要考虑分配空间问题。即从链表中备用空间里拿出一空间,具体如下:
/*静态链表返回空闲空间下标,也是进行插入元素的准备操作(类似动态链表的分配空间)*/
int Malloc_SL(StaticLinkList space) //返回分配的结点下标,否则返回0
{
int i = space[0].cur; //当前数组第一个元素的cur的值,该值代表备用链表起始位置
if (space[0].cur)
{
space[0].cur = space[i].cur; //由于要出空闲链表中拿一个空间出来,因此需要将它相邻的空间的下标给头节点的cur
}
return i;
}
2)插入新元素,具体我们这里举例将在“乙” “丁”中间插入“丙”
/*静态链表的插入操作*/
Status Listinsert(StaticLinkList &L, int i, ElemType e)
{
/*在L链表中第i个元素前面插入新的数据元素*/
int k, j;
k = MAXSIZE - 1; //k为最后一个元素的下标
if (i<1 || i>ListLength(L) + 1) //插入位置错误,异常抛出
return ERROR;
j = Malloc_SL(L);
if (j)
{
L[j].data = e; //将新值放入刚刚知道的空闲下标的数组元素值中
/*静态链表插入新值时,不会改变插入值在数组中的位置,只会跟新插入前后数据的游标cur值*/
for (int n = 1; n <= i - 1; n++) //此时k为第i元素之前的下标
k = L[k].cur;
L[j].cur = L[k].cur; //把第i前元素的cur赋值给新元素的cur
L[k].cur = j; //再更新第i前元素的cur
return OK;
}
return ERROR;
}
5、静态链表删除操作
1)删除时,需要考虑第一删除位置合不合理?
2)删除元素数据后,数据空间的回收问题,即怎么将它恢复到备用链表中?
3)删除元素的目标链表实际元素数据长度?多长问题
4)就是删除元素的前后元素游标怎么变化?下面以删除“甲”为例
//将删除节点空间进行回收,回收为备用链表中
void Free_SL(StaticLinkList &space, int k) //k表示删除元素再链表中的下标
{
/*如何回收?即将该节点作为备用链表的第1节点*/
space[k].cur = space[0].cur; //将头节点中的指向备用链表的第一节点下标赋值为当前节点的游标值
space[0].cur = k; //更新头节点游标值
}
/*返回静态链表中数据元素的个数*/
int ListLength(StaticLinkList L)
{
int count = 0; //计数器
int i = L[MAXSIZE - 1].cur; //得到链表中第1有值元素的下标
while (i)
{
i = L[i].cur;
count++;
}
return count;
}
/*删除L中第i元素的数据e*/
Status ListDelete(StaticLinkList &L, int i)
{
int j, k;
if (i<1 || i>ListLength(L))
return ERROR;
k = MAXSIZE - 1;
for (j = 1; j <= i - 1; j++) //找到第i元素之前元素的下标
k = L[k].cur;
j = L[k].cur; //第i元素之前元素的游标
L[k].cur = L[j].cur; //将第i元素之后的元素的下标 交给 第i前元素的游标
Free_SL(L, j);
return OK;
}
6、完整代码
#include"iostream"
using namespace std;
/*
线性表的操作包括如下几种
(1) InitList(&L) 初始化,构造一个空的线性表
(2) ListEmpty(&L) 判断线性表是否为空,true or flase
(3) ClearList(&L) 清空线性表中的内容
(4) GetElem(&L,i,e) 返回线性表i位置上的元素值,通过e返回
(5) LocateElem(&L,e) 在线性表中找到与e相同的元素,成功则返回其序号,否则返回0表示失败
(9) Listinsert(&L,i,e) 如果线性表存在了,而且i符合条件,则在i位置插入一个元素e
(10)ListDelete(&L,i,) 删除i位置上的元素
(5) ListLength(L) 返回线性表的长度
*/
#define MAXSIZE 1000
#define OK 1
#define ERROR 0
typedef int Status;
typedef int ElemType;
struct Component{
ElemType data;
int cur; //代表游标,为0时无指向
};
typedef Component StaticLinkList[MAXSIZE]; //typedef可以掩饰复合类型,如指针和数组
/*初始化数组线性表,也是将数组链接成备用链表,其中space[0].cur代表头指针*/
Status InitList(StaticLinkList &space)
{
for (int i = 0; i < MAXSIZE - 1; i++)
space[i].cur = i + 1;
space[MAXSIZE - 1].cur = 0; //目前静态链表为空,所以最后一元素的cur=0
return OK;
}
/*静态链表返回空闲空间下标,也是进行插入元素的准备操作(类似动态链表的分配空间)*/
int Malloc_SL(StaticLinkList space) //返回分配的结点下标,否则返回0
{
int i = space[0].cur; //当前数组第一个元素的cur的值,该值代表备用链表起始位置
if (space[0].cur)
{
space[0].cur = space[i].cur; //由于要出空闲链表中拿一个空间出来,因此需要将它相邻的空间的下标给头节点的cur
}
return i;
}
/*返回静态链表中数据元素的个数*/
int ListLength(StaticLinkList L)
{
int count = 0; //计数器
int i = L[MAXSIZE - 1].cur; //得到链表中第1有值元素的下标
while (i)
{
i = L[i].cur;
count++;
}
return count;
}
/*静态链表的插入操作*/
Status Listinsert(StaticLinkList &L, int i, ElemType e)
{
/*在L链表中第i个元素前面插入新的数据元素*/
/*注意:在开展插入操作时,静态链表不能为空链表,至少长度>=1*/
int k, j;
k = MAXSIZE - 1; //k为最后一个元素的下标
if (i<1 || i>ListLength(L) + 1) //插入位置错误,异常抛出
return ERROR;
j = Malloc_SL(L);
if (j)
{
L[j].data = e; //将新值放入刚刚知道的空闲下标的数组元素值中
/*静态链表插入新值时,不会改变插入值在数组中的位置,只会跟新插入前后数据的游标cur值*/
for (int n = 1; n <= i - 1; n++) //此时k为第i元素之前的下标
k = L[k].cur;
L[j].cur = L[k].cur; //把第i前元素的cur赋值给新元素的cur
L[k].cur = j; //再更新第i前元素的cur
return OK;
}
return ERROR;
}
//将删除节点空间进行回收,回收为备用链表中
void Free_SL(StaticLinkList &space, int k) //k表示删除元素再链表中的下标
{
/*如何回收?即将该节点作为备用链表的第1节点*/
space[k].cur = space[0].cur; //将头节点中的指向备用链表的第一节点下标赋值为当前节点的游标值
space[0].cur = k; //更新头节点游标值
}
/*删除L中第i元素的数据e*/
Status ListDelete(StaticLinkList &L, int i)
{
int j, k;
if (i<1 || i>ListLength(L))
return ERROR;
k = MAXSIZE - 1;
for (j = 1; j <= i - 1; j++) //找到第i元素之前元素的下标
k = L[k].cur;
j = L[k].cur; //第i元素之前元素的游标
L[k].cur = L[j].cur; //将第i元素之后的元素的下标 交给 第i前元素的游标
Free_SL(L, j);
return OK;
}
int main()
{
StaticLinkList mylist;
InitList(mylist); //初始化静态链表,空闲链表此时,默认头节点为0
int h = Malloc_SL(mylist);
mylist[h].cur = 0; //初始链表元素存储头节点h=1,
mylist[h].data = 50;
mylist[999].cur = h; //更新数组最后元素的指向的第1元素下标,cur=1
Listinsert(mylist,1,1);
Listinsert(mylist, 2, 2);
Listinsert(mylist, 2, 3);
//遍历并输出该链表上的所有数据
cout << "遍历并输出该链表上的所有数据:" << endl;
int i = mylist[999].cur;
while (i)
{
cout << mylist[i].data << " ";
i = mylist[i].cur;
}
cout << endl;
cout << "please hello world!" << endl;
ListDelete(mylist, 2); //删除元素
//遍历并输出该链表上的所有数据
cout << "遍历并输出该链表上的所有数据:" << endl;
i = mylist[999].cur;
while (i)
{
cout << mylist[i].data << " ";
i = mylist[i].cur;
}
cout << endl;
system("pause");
return 0;
}
}
输出结果:
遍历并输出该链表上的所有数据:
1 3 2 50
please hello world!
遍历并输出该链表上的所有数据:
1 2 50