目录
1.静态链表的概念
因为有些语言没有指针,所以难以实现普通链表,静态链表就是用来解决这一问题的有力工具,静态链表使用数组来实现链表。静态链表用游标来代替普通链表的指针域,并且用下标代替普通链表的结点地址。
下面是一个静态链表的示意图
结合上图来分析
(1)静态链表的每个结点包含三个要素:下标、数据、游标。下标表示当前元素所处的位置,类似于链表的结点地址;游标存放下一个有效结点的下标,类似于链表的指针域。例如下标为2的元素,存放的数据为B,游标值为3,说明下一个结点就是C。
(2)有效结点:含有数据的结点。
(3)第一个结点和最后一个结点不存放数据。第一个结点(下标为0)存放备用链表的第一个结点的下标;最后一个结点相当于链表的头结点,该结点的游标存放第一个有效结点的下标,当链表为空的时候,游标为0。其他结点的游标都存放下一个结点的下标。
(4)最后一个有效结点的游标为0,说明下一个结点为空。
2.静态链表的存储结构
使用结构体数组类型的数据来存储静态链表:
typedef struct
{
ElemType data;//数据
int cur; //游标
}StaticLinkList[MAXSIZE];
//一维数组类型的定义方法:定义了一个元素类型为结构体,含有MAXSIZE个元素的数组类型StaticLinkList
3.静态链表的基本操作
(1)初始化:清空静态链表,建立一个空的静态链表。
(2)插入结点:在指定位置插入新结点。
(3)删除结点:删除指定位置的结点,并且回收该节点的存储空间以便下次再用。
(4)新建静态链表:将一系列元素放入空表。
(5)获取静态链表的长度:得到静态链表中有效元素的个数。
4.静态链表的编程实现
在程序的设计思路上,静态链表的新建、插入和删除等操作跟普通链表差不多,只需要注意静态链表的一些约定条件即可。
4.1C语言实现静态链表
(1)静态链表的声明 staticlinklist.h
#ifndef STATICLINKLIST_H
#define STATICLINKLIST_H
#define MAXSIZE 1000
typedef int ElemType;
typedef enum Bool
{
FALSE,TRUE//枚举默认值从0开始,依次加1
}Bool;
typedef struct
{
ElemType data;//数据
int cur; //游标
}StaticLinkList[MAXSIZE];
//一维数组类型的定义方法:定义了一个元素类型为结构体,含有MAXSIZE个元素的数组类型StaticLinkList
Bool InitList(StaticLinkList L);//初始化静态表(也可以用来清空静态表)
int Malloc_SLL(StaticLinkList L);//申请新结点
Bool ListInsert(StaticLinkList L,int i,ElemType e);//指定位置插入新结点
Bool ListDelete(StaticLinkList L,int i,ElemType* e);//删除指定位置的结点
void Free_SLL(StaticLinkList L,int k);//将已删除的结点释放,方便下次再用
int ListLength(StaticLinkList L);//获取静态表的结点数目
void display(StaticLinkList L);//显示当前的顺序表
Bool CreatList(StaticLinkList L, int n);//用随机数新建一个静态表,其结点个数为n
#endif // STATICLINKLIST_H
(2) 静态链表的实现 staticlinklist.c
#include "staticlinklist.h"
Bool InitList(StaticLinkList L)
{
int i;
for(i=0;i<MAXSIZE-1;i++)
L[i].cur=i+1;//第0个元素的游标指向第1个有效元素的下标,有效数据是从1开始的(第一个元素不存储信息)
L[MAXSIZE-1].cur=0;
return TRUE;
}
/*获取新节点
*1.得到备用结点的下标
*2.更新当前链表的第一个结点的游标
*/
int Malloc_SLL(StaticLinkList L)
{
int i=L[0].cur;//获取备用结点的下标
if(i)//备用链表不是空链表
L[0].cur=L[i].cur;//更新备用链表头结点
return i;
}
//将备用链表的第一个结点作为待插入的新结点
//插入方法:
//1.将新结点的下标值放入待插入位置的前一个结点的游标;
//2.新结点的游标值为,插入正确位置后,下一个结点的下标值。
Bool ListInsert(StaticLinkList L,int i,ElemType e)
{
int j,k,l;
k=MAXSIZE-1;//最后一个结点存放当前链表的起始下标
if(i<1||i>ListLength(L)+1)//插入位置不合理
return FALSE;
j=Malloc_SLL(L);//新结点下标
if(j)//新节点不为空
{
L[j].data=e;
for(l=0;l<i-1;l++)//这个下标值对应的结点的游标存放待插入位置的下标(包括第一个位置)
k=L[k].cur;
L[j].cur=L[k].cur;//新结点的游标存放原来处于第i个位置的结点下标值(即当前结点的游标)
L[k].cur=j;//更新当前结点的游标值
return TRUE;
}
return FALSE;
}
Bool ListDelete(StaticLinkList L,int i,ElemType* e)
{
int j,k;
k=MAXSIZE-1;//待删除的前一个结点下标
if(i<1||i>ListLength(L))//删除位置不合理
return FALSE;
for(j=0;j<i-1;j++)//寻找删除点的下标值
k=L[k].cur;
j=L[k].cur;//待删除结点的下标
L[k].cur=L[j].cur;//待删除结点的游标值(下个结点的位置)赋给待删除结点前一个结点的游标
*e=L[j].data;
Free_SLL(L,j);
return TRUE;
}
void Free_SLL(StaticLinkList L,int k)
{
L[k].cur=L[0].cur;//待删除结点作为新的备用链表头结点,他的游标为原来的头结点下标
L[0].cur=k;//更新备用链表头结点的下标值
}
int ListLength(StaticLinkList L)
{
int i=0,j;
j=L[MAXSIZE-1].cur;//头结点的下标值
while (j)
{
i++;
j=L[j].cur;
}
return i;
}
void display(StaticLinkList L)
{
int j;
j=L[MAXSIZE-1].cur;//头结点的下标值
if(j==0)
printf("这是一个空的静态表");
while (j)
{
printf("%d ",L[j].data);
j=L[j].cur;
}
printf("\n\n");
}
Bool CreatList(StaticLinkList L, int n)//新建一个静态表
{
int i,j;
if(n<1||n>MAXSIZE-2)//结点数目不合理
return FALSE;
if(L[MAXSIZE-1].cur)//当前静态表非空
return FALSE;
j=L[0].cur;//起始结点的下标
for(i=1;i<n;i++)
{
L[j].data=rand()%100;
// printf("新建元素%d ",L[j].data);
j=L[j].cur;
}
L[j].data=rand()%100+1;//最后一个元素
L[j].cur=0;//最后一个元素的游标为0
L[MAXSIZE-1].cur=L[0].cur;//最后一个结点存放当前链表的起始下标
L[0].cur=j;//最后一个元素的下标
// printf("新建元素%d \n",L[i].data);
return TRUE;
}
(3)测试程序 main.c
#include <stdio.h>
#include "staticlinklist.h"
int main()
{
StaticLinkList List;
int i;
ElemType e;
printf(" 1.初始化静态链表\n");
InitList(List);
display(List);
printf("\n 2.插入5个结点\n");
for(i=0;i<5;i++)
{
printf("插入第%d个结点:%d\n",i+1,i);
ListInsert(List,i+1,i);
display(List);
}
printf("\n 3.删除5个结点\n");
for(i=0;i<5;i++)
{
ListDelete(List,1,&e);
printf("删除第%d个结点:%d\n",i+1,e);
display(List);
}
// InitList(List);新建链表的时候不要求链表必须初始化,只要是空静态表就能新建
printf("\n 4.随机创建10个结点\n");
CreatList(List,10);
display(List);
printf("\n 5.清空静态表\n");
InitList(List);
display(List);
return 0;
}
4.2C++实现静态链表
(1)静态链表的声明 staticlinklist.h
#ifndef STATICLINKLIST_H
#define STATICLINKLIST_H
#define MAXSIZE 1000
template<typename ElemType>//用类模板来代替typedef int ElemType;
class staticLinkList
{
public:
staticLinkList();//初始化静态表
typedef struct
{
ElemType data;//数据
int cur; //游标
}Node;
int Malloc_SLL();//申请新结点
bool ListInsert(int i,ElemType e);//指定位置插入新结点
bool ListDelete(int i,ElemType* e);//删除指定位置的结点
void Free_SLL(int k);//将已删除的结点释放,方便下次再用
int ListLength();//获取静态表的结点数目
void display();//显示当前的顺序表
bool CreatList(ElemType a[],int n);//用随机数新建一个静态表,其结点个数为n
void ListDelete();
private:
Node L[MAXSIZE];
};
#endif // STATICLINKLIST_H
(2)静态链表的实现 staticlinklist.cpp
#include <iostream>
#include "staticlinklist.h"
using namespace std;
template<typename ElemType>
staticLinkList<ElemType>::staticLinkList()
{
int i;
for(i=0;i<MAXSIZE-1;i++)
L[i].cur=i+1;//第0个元素的游标指向第1个有效元素的下标,有效数据是从1开始的(第一个元素不存储信息)
L[MAXSIZE-1].cur=0;
}
template<typename ElemType>
int staticLinkList<ElemType>::Malloc_SLL()
{
int i=L[0].cur;//获取备用结点的下标
if(i)//备用链表不是空链表
L[0].cur=L[i].cur;//更新备用链表头结点
return i;
}
template<typename ElemType>
bool staticLinkList<ElemType>::ListInsert(int i,ElemType e)
{
int j,k,l;
k=MAXSIZE-1;//最后一个结点存放当前链表的起始下标
if(i<1||i>ListLength()+1)//插入位置不合理
return false;
j=Malloc_SLL();//新结点下标
if(j)//新节点不为空
{
L[j].data=e;
for(l=0;l<i-1;l++)//这个下标值对应的结点的游标存放待插入位置的下标(包括第一个位置)
k=L[k].cur;
L[j].cur=L[k].cur;//新结点的游标存放原来处于第i个位置的结点下标值(即当前结点的游标)
L[k].cur=j;//更新当前结点的游标值
return true;
}
return false;
}
template<typename ElemType>
bool staticLinkList<ElemType>::ListDelete(int i,ElemType* e)
{
int j,k;
k=MAXSIZE-1;//待删除的前一个结点下标
if(i<1||i>ListLength())//删除位置不合理
return false;
for(j=0;j<i-1;j++)//寻找删除点的下标值
k=L[k].cur;
j=L[k].cur;//待删除结点的下标
L[k].cur=L[j].cur;//待删除结点的游标值(下个结点的位置)赋给待删除结点前一个结点的游标
*e=L[j].data;
Free_SLL(j);
return true;
}
template<typename ElemType>
void staticLinkList<ElemType>::Free_SLL(int k)
{
L[k].cur=L[0].cur;//待删除结点作为新的备用链表头结点,他的游标为原来的头结点下标
L[0].cur=k;//更新备用链表头结点的下标值
}
template<typename ElemType>
int staticLinkList<ElemType>::ListLength()
{
int i=0,j;
j=L[MAXSIZE-1].cur;//头结点的下标值
while (j)
{
i++;
j=L[j].cur;
}
return i;
}
template<typename ElemType>
void staticLinkList<ElemType>::display()
{
int j;
j=L[MAXSIZE-1].cur;//头结点的下标值
if(j==0)
printf("这是一个空的静态表");
while (j)
{
cout<<L[j].data<<" ";
j=L[j].cur;
}
cout<<endl<<endl;
}
template<typename ElemType>
bool staticLinkList<ElemType>::CreatList(ElemType a[],int n)
{
int i,j;
if(n<1||n>MAXSIZE-2)//结点数目不合理
return false;
if(L[MAXSIZE-1].cur)//当前静态表非空
return false;
j=L[0].cur;//起始结点的下标
for(i=0;i<n-1;i++)
{
L[j].data=a[i];
j=L[j].cur;
}
L[j].data=a[i];//最后一个元素
L[j].cur=0;//最后一个元素的游标为0
L[MAXSIZE-1].cur=L[0].cur;//最后一个结点存放当前链表的起始下标
L[0].cur=j;//最后一个元素的下标
return true;
}
template<typename ElemType>
void staticLinkList<ElemType>::ListDelete()
{
int i;
for(i=0;i<MAXSIZE-1;i++)
L[i].cur=i+1;//第0个元素的游标指向第1个有效元素的下标,有效数据是从1开始的(第一个元素不存储信息)
L[MAXSIZE-1].cur=0;
}
(3)测试程序 main.cpp
#include <iostream>
#include "staticlinklist.cpp"//类模板要求在编译的时候能同时访问模板定义和方法定义
using namespace std;
int main()
{
staticLinkList<int> List;//模板类
int i,e;
cout<<" 1.初始化静态链表\n"<<endl;
List.display();
cout<<"\n 2.插入5个结点\n"<<endl;
for(i=0;i<5;i++)
{
cout<<"插入第"<<i+1<<"个结点:"<<i<<endl;
List.ListInsert(i+1,i);
List.display();
}
cout<<"\n 3.删除5个结点\n"<<endl;
for(i=0;i<5;i++)
{
List.ListDelete(1,&e);
cout<<"删除第"<<i+1<<"个结点:"<<e<<endl;
List.display();
}
cout<<"\n 4.新建静态表\n"<<endl;
int a[10];
for(i=0;i<10;i++)
a[i]=rand()%100;
List.CreatList(a,10);
List.display();
cout<<"\n 5.清空静态表\n"<<endl;
List.ListDelete();
List.display();
return 0;
}
4.3测试结果
5.静态链表的特点
优点:
与单链表类似,插入和删除的时候不需要移动大量元素;
缺点:
1.没有解决连续存储分配(数组)带来的表长难以确定的问题;
2.失去了顺序存储结构随机存取的特性。
6.参考资料
[1]小甲鱼的教学视频:https://www.bilibili.com/video/av2975983/?p=16
[2]部分图片的来源:https://www.2cto.com/kf/201512/453071.html