静态链表
一、静态链表定义
用数组来描述的链表
二、静态链表存储数据类型定义
typedef int ElemType;//定义存储数据
静态链表结点结构
数据域:用于存储数据元素的值;
游标:其实就是数组下标,表示直接后继元素所在数组中的位置;
线性表的静态单链表存储结构
typedef struct
{
ElemType data;//数据域
int cur;//游标(Cursor类似于指针域),相当于单链表中的next指针,存放该元素的后继在数组中的下标,为0时表示无指向
}component, SLinkList[MAX_SIZE];
//component 是变量类型,而SLinkList[MAX_SIZE]是component类型的长度为MAX_SIZE数组。
//SLinkList L 等价于 component L[MAX_SIZE];
静态链表的存储数据思路
1、一般我们对数组第一个和最后一个元素作为特殊元素处理,不存数据。
2、通常把未使用的数组元素称为备用链表。
3、而数组第一个元素,即下标为0的元素的cur就存放备用链表的第一个结点的下标;而数组的最后一个元素的cur则存放第一个有数值的元素的下标,相当于单链表中的头结点作用,当整个链表为空时,则为0
静态表的实现
1、构造一个空的链表L
void InitList(SLinkList L)
{ // 构造一个空的链表L,表头为L的最后一个单元L[MAX_SIZE-1],其余单元链成
// 一个备用链表,表头为L的第一个单元L[0],“0”表示空指针
int i;
L[MAX_SIZE - 1].cur = 0; // L的最后一个单元为空链表的表头,初始化表头指向空
for (i = 0; i < MAX_SIZE - 2; i++) // 将其余单元链接成以L[0]为表头的备用链表
L[i].cur = i + 1;
L[MAX_SIZE - 2].cur = 0;
}
2、获取静态链表的长度
从第一个结点元素开始计数直到末尾游标为0的结点
int ListLength(SLinkList L)
{ // 返回L中数据元素个数
int j = 0, i = L[MAX_SIZE - 1].cur; // i指向第一个元素(i此时为头结点的cur值)
while (i) // 没到静态链表尾
{
i = L[i].cur; // 指向下一个元素
j++;
}
return j;
}
3、判断静态链表是否为空表
如果数据链表的头结点的游标为0,则静态链表为空
bool ListEmpty(SLinkList L)
{ // 若L是空表,返回TRUE;否则返回FALSE
if (L[MAX_SIZE - 1].cur == 0) // 头结点游标cur为0则是空表
return true;
else
return false;
}
4、向数据链表中追加元素
bool ListAddElem(SLinkList L,ElemType e)
{
int i=MAX_SIZE-1,k=0;
while (i != 0)
{
k = i;//保存倒数第二个结点的游标,此游标是最后一个节点的下标
i = L[i].cur;
}
//创建新节点
int NewCur = 0;
NewCur = L[0].cur;//获取备用链表的第一个结点元素
if (NewCur == 0)//如果备用链表为空,追加失败!
{
printf("备用链表为空,数据链表已满,追加失败\n");
return false;
}
else //如果备用链表不为空,将游标为NewCur的结点追加数据链表的尾部!
{
L[k].cur = NewCur;//追加到尾部
L[0].cur = L[NewCur].cur;//备用链表头结点游标指向下一个结点
L[NewCur].data = e;//作为数据链表的尾部结点,数据域要赋值
L[NewCur].cur = 0;//作为数据链表的尾部结点,游标要置0
return true;
}
}
5、遍历静态链表
void ListTraverse(SLinkList L)
{
int i = L[MAX_SIZE - 1].cur;//游标指向首节点
if (ListEmpty(L))//静态表为空结束程序
{
printf("链表为空,遍历失败\n");
exit(-1);
}
else
{
while (i != 0)//遍历输出
{
printf(" %d", L[i].data);
i = L[i].cur;
}
printf("\n");
}
}
6、用e返回数据链表L中第i个结点元素的值
bool GetElem(SLinkList L, int i, ElemType &e)
{ // 用e返回L中第i个结点元素的值
int l, k = MAX_SIZE - 1; // k指向头结点
if (i<1 || i>ListLength(L)) // i值不合理
return false;
for (l = 1; l <= i; l++) // 移动i个元素
k = L[k].cur;
e = L[k].data;
return true;
}
7、在静态单链表L中查找第1个值为e的元素。
int LocateElem(SLinkList L, ElemType e)
{ // 在静态单链表L中查找第1个值为e的元素。
// 若找到,则返回它在L中的位序,否则返回0。(与其它LocateElem()的定义不同)
int i = L[MAX_SIZE-1].cur; // i指示表中第一个结点
while (i&&L[i].data != e) // 在表中顺链查找(e不能是字符串)
i = L[i].cur;
return i;
}
8、找到值为cur_e的前驱结点值
bool PriorElem(SLinkList L, ElemType cur_e, ElemType &pre_e)
{ // 初始条件:在L中表头位序为[MAX_SIZE - 1]的静态单链表已存在
// 操作结果:若cur_e是此单链表的数据元素,且不是第一个,
// 则用pre_e返回它的前驱,否则操作失败,pre_e无定义
int j, i = L[MAX_SIZE - 1].cur; // i为链表第一个结点的位置
if (L[i].data == cur_e)
{
printf("链表首结点没有前驱\n");
return false;
}
do
{ // 向后移动结点
j = i;
i = L[i].cur;
} while (i&&cur_e != L[i].data);//判断后移一个结点元素的值是否等于cur_e
if (i) //是否移动到尾结点之后(游标值为尾结点的cur值) 如果没有则,找到该元素,并取出其前驱结点元素值,如果是返回false
{
pre_e = L[j].data;
return true;
}
return false;
}
9、查找链表中第一个值为cur_e的结点元素的后继结点值
bool NextElem(SLinkList L, ElemType cur_e, ElemType &next_e)
{ // 初始条件:在静态单链表L已存在
// 操作结果:若cur_e是此单链表的数据元素,且不是最后一个,
// 则用next_e返回它的后继,否则操作失败,next_e无定义
int i;
i = LocateElem(L, cur_e); // 在链表中查找第一个值为cur_e的元素的位置
if (i) // 在静态单链表中存在元素cur_e
{
i = L[i].cur; // cur_e的后继的位置
if (i) // cur_e有后继
{
next_e = L[i].data;
return true; // cur_e元素有后继
}
}
return false; // L不存在cur_e元素,cur_e元素无后继
}
10、在静态链表中的第i个结点前插入一个值为e的结点
bool ListInsert(SLinkList L, int i, ElemType e)
{
int k = 0,j = MAX_SIZE -1;//游标j指向数据链表头结点
if (i <= 0 )
{
return false;
}
while (k < i - 1) // 找到第i-1个结点
{
j = L[j].cur;
k++;
}
if (k > i - 1)
{
return false;
}
//获取新节点
int NewCur = 0;
NewCur = L[0].cur;//获取备用链表的第一个结点元素
if (NewCur == 0)//如果备用链表为空,追加失败!
{
printf("备用链表为空,数据链表已满,追加失败\n");
return false;
}
else //如果备用链表不为空,将游标为NewCur的结点追加数据链表的尾部!
{
L[0].cur = L[NewCur].cur;//备用链表头结点游标指向备用链表的第二个结点
L[NewCur].data = e;//新节点数据域要赋值
L[NewCur].cur = L[j].cur;//第i个结点挂载到新结点上
L[j].cur = NewCur;//新节点挂载到第(i-1)个结点
return true;
}
11、删除第i个元素,并返回删除结点的值
bool ListDelete(SLinkList L, int i, ElemType &e)
{ // 删除在L中第i个数据元素e,并返回其值
int j, k = MAX_SIZE - 1; // k指向表头
if (i<1 || i>ListLength(L))//排除不在链表中的元素
return false;
for (j = 1; j < i; j++) // 移动i-1个元素
k = L[k].cur;//找到要删除节点元素的前一个结点
j = L[k].cur;//游标j指向第i个节点
L[k].cur = L[j].cur;//第i-1个节点的游标指向第i+1个节点
e = L[j].data;//获取到要删除的节点i的值
Free(L, j);//将要删除的结点给插入到备用链表的第一个结点前
return true;
}
11.1、向备用链表的首结点前插入游标为k的结点
void Free(SLinkList L, int k) //
{ // 将下标为k的空闲结点回收到备用链表(成为备用链表的第一个结点)
L[k].cur = L[0].cur; // 回收结点的"游标"指向备用链表的第一个结点
L[0].cur = k; // 备用链表的头结点指向新回收的结点
}
最后给出全部代码
#include<stdio.h> // EOF(=^Z或F6),NULL
#include<stdlib.h> // atoi()
#include<malloc.h> // malloc()等
#define MAX_SIZE 100 // 链表的最大长度
typedef int ElemType;//定义存储数据
// c2-3.h 线性表的静态单链表存储结构
typedef struct
{
ElemType data;//数据域
int cur;//游标(Cursor类似于指针域),相当于单链表中的next指针,存放该元素的后继在数组中的下标,为0时表示无指向
}component, SLinkList[MAX_SIZE];
//component 是变量类型,而SLinkList[MAX_SIZE]是component类型的长度为MAX_SIZE数组。
//SLinkList L 等价于 component L[MAX_SIZE];
//1、构造一个空的链表L
void InitList(SLinkList L);
//2、获取静态链表长度
int ListLength(SLinkList L);
//3、判断静态链表是否为空表
bool ListEmpty(SLinkList L);
//4、添加元素
bool ListAddElem(SLinkList L, ElemType e);
//5、遍历静态链表
void ListTraverse(SLinkList L);
//6、用e返回L中第i个结点元素的值
bool GetElem(SLinkList L, int i, ElemType &e);
//7、在静态单链表L中查找第1个值为e的元素。
int LocateElem(SLinkList L, ElemType e);
//8、找到值位cur_e的前驱结点值
bool PriorElem(SLinkList L, ElemType cur_e, ElemType &pre_e);
//9、查找链表中第一个值为cur_e的结点元素的后继结点值
bool NextElem(SLinkList L, ElemType cur_e, ElemType &next_e);
//10、在静态链表中的第i个结点前插入一个值为e的结点
bool ListInsert(SLinkList L, int i, ElemType e);
//11、删除第i个元素,并返回删除结点的值
bool ListDelete(SLinkList L, int i, ElemType &e);
//11.1、向备用链表的首结点前插入游标为k的结点
void Free(SLinkList L, int k);
int main()
{
SLinkList L;//定义一个长度为100的数据类型为component的数组
InitList(L);
printf("静态链表的长度为 %d\n",ListLength(L));
int length = 10;
for (int i = 0; i < length; i++)
{
ListAddElem(L, 2 * i);
}
printf("静态链表的长度为 %d\n", ListLength(L));
ListTraverse(L);
int e,e1 = 88,cur_e = 10,pre_e = 0, next_e = 0;
GetElem(L, 10, e);
printf("获取到的链表的第10个结点元素值为 %d\n",e);
printf("查找到的值为 %d 的结点的序位为 %d\n",e,LocateElem( L, e));
PriorElem(L, cur_e, pre_e);
printf("结点值为%d 的结点的前驱值为 %d\n", cur_e,pre_e);
NextElem(L, cur_e, next_e);
printf("结点值为%d 的结点的后继值为 %d\n", cur_e,next_e);
ListInsert(L, 9, e1);
printf("插入的元素为%d\n", e);
ListTraverse(L);
ListDelete(L, 9, e);
printf("删除的元素为%d\n", e);
ListTraverse(L);
return 0;
}
//1、构造一个空的链表L
void InitList(SLinkList L)
{ // 构造一个空的链表L,表头为L的最后一个单元L[MAX_SIZE-1],其余单元链成
// 一个备用链表,表头为L的第一个单元L[0],“0”表示空指针
int i;
L[MAX_SIZE - 1].cur = 0; // L的最后一个单元为空链表的表头,初始化表头指向空
for (i = 0; i < MAX_SIZE - 2; i++) // 将其余单元链接成以L[0]为表头的备用链表
L[i].cur = i + 1;
L[MAX_SIZE - 2].cur = 0;
}
//2、获取静态链表长度
int ListLength(SLinkList L)
{ // 返回L中数据元素个数
int j = 0, i = L[MAX_SIZE - 1].cur; // i指向第一个元素(i此时为头结点的cur值)
while (i) // 没到静态链表尾
{
i = L[i].cur; // 指向下一个元素
j++;
}
return j;
}
//3、判断静态链表是否为空表
bool ListEmpty(SLinkList L)
{ // 若L是空表,返回TRUE;否则返回FALSE
if (L[MAX_SIZE - 1].cur == 0) // 头结点游标cur为0则是空表
return true;
else
return false;
}
//4、向数据链表中追加元素
bool ListAddElem(SLinkList L,ElemType e)
{
int i=MAX_SIZE-1,k=0;
while (i != 0)
{
k = i;//保存倒数第二个结点的游标,此游标是最后一个节点的下标
i = L[i].cur;
}
//创建新节点
int NewCur = 0;
NewCur = L[0].cur;//获取备用链表的第一个结点元素
if (NewCur == 0)//如果备用链表为空,追加失败!
{
printf("备用链表为空,数据链表已满,追加失败\n");
return false;
}
else //如果备用链表不为空,将游标为NewCur的结点追加数据链表的尾部!
{
L[k].cur = NewCur;//追加到尾部
L[0].cur = L[NewCur].cur;//备用链表头结点游标指向下一个结点
L[NewCur].data = e;//作为数据链表的尾部结点,数据域要赋值
L[NewCur].cur = 0;//作为数据链表的尾部结点,游标要置0
return true;
}
}
//5、遍历静态链表
void ListTraverse(SLinkList L)
{
int i = L[MAX_SIZE - 1].cur;//游标指向首节点
if (ListEmpty(L))//静态表为空结束程序
{
printf("链表为空,遍历失败\n");
exit(-1);
}
else
{
while (i != 0)//遍历输出
{
printf(" %d", L[i].data);
i = L[i].cur;
}
printf("\n");
}
}
//6、用e返回数据链表L中第i个结点元素的值
bool GetElem(SLinkList L, int i, ElemType &e)
{ // 用e返回L中第i个结点元素的值
int l, k = MAX_SIZE - 1; // k指向头结点
if (i<1 || i>ListLength(L)) // i值不合理
return false;
for (l = 1; l <= i; l++) // 移动i个元素
k = L[k].cur;
e = L[k].data;
return true;
}
//7、在静态单链表L中查找第1个值为e的元素。
int LocateElem(SLinkList L, ElemType e)
{ // 在静态单链表L中查找第1个值为e的元素。
// 若找到,则返回它在L中的位序,否则返回0。(与其它LocateElem()的定义不同)
int i = L[MAX_SIZE-1].cur; // i指示表中第一个结点
while (i&&L[i].data != e) // 在表中顺链查找(e不能是字符串)
i = L[i].cur;
return i;
}
//8、找到值位cur_e的前驱结点值
bool PriorElem(SLinkList L, ElemType cur_e, ElemType &pre_e)
{ // 初始条件:在L中表头位序为[MAX_SIZE - 1]的静态单链表已存在
// 操作结果:若cur_e是此单链表的数据元素,且不是第一个,
// 则用pre_e返回它的前驱,否则操作失败,pre_e无定义
int j, i = L[MAX_SIZE - 1].cur; // i为链表第一个结点的位置
if (L[i].data == cur_e)
{
printf("链表首结点没有前驱\n");
return false;
}
do
{ // 向后移动结点
j = i;
i = L[i].cur;
} while (i&&cur_e != L[i].data);//判断后移一个结点元素的值是否等于cur_e
if (i) //是否移动到尾结点之后(游标值为尾结点的cur值) 如果没有则,找到该元素,并取出其前驱结点元素值,如果是返回false
{
pre_e = L[j].data;
return true;
}
return false;
}
//9、查找链表中第一个值为cur_e的结点元素的后继结点值
bool NextElem(SLinkList L, ElemType cur_e, ElemType &next_e)
{ // 初始条件:在静态单链表L已存在
// 操作结果:若cur_e是此单链表的数据元素,且不是最后一个,
// 则用next_e返回它的后继,否则操作失败,next_e无定义
int i;
i = LocateElem(L, cur_e); // 在链表中查找第一个值为cur_e的元素的位置
if (i) // 在静态单链表中存在元素cur_e
{
i = L[i].cur; // cur_e的后继的位置
if (i) // cur_e有后继
{
next_e = L[i].data;
return true; // cur_e元素有后继
}
}
return false; // L不存在cur_e元素,cur_e元素无后继
}
//10、在静态链表中的第i个结点前插入一个值为e的结点
bool ListInsert(SLinkList L, int i, ElemType e)
{
int k = 0,j = MAX_SIZE -1;//游标j指向数据链表头结点
if (i <= 0 )
{
return false;
}
while (k < i - 1) // 找到第i-1个结点
{
j = L[j].cur;
k++;
}
if (k > i - 1)
{
return false;
}
//获取新节点
int NewCur = 0;
NewCur = L[0].cur;//获取备用链表的第一个结点元素
if (NewCur == 0)//如果备用链表为空,追加失败!
{
printf("备用链表为空,数据链表已满,追加失败\n");
return false;
}
else //如果备用链表不为空,将游标为NewCur的结点追加数据链表的尾部!
{
L[0].cur = L[NewCur].cur;//备用链表头结点游标指向备用链表的第二个结点
L[NewCur].data = e;//新节点数据域要赋值
L[NewCur].cur = L[j].cur;//第i个结点挂载到新结点上
L[j].cur = NewCur;//新节点挂载到第(i-1)个结点
return true;
}
}
//10.1、从备用链表中取出其首结点,
int Malloc(SLinkList L)
{ // 若备用链表非空,则返回分配的结点下标(备用链表的第一个结点),否则返回0
int NewCur = L[0].cur;
if (NewCur) // 备用链表非空
L[0].cur = L[NewCur].cur; // 备用链表的头结点指向原备用链表的第二个结点
return NewCur; // 返回新开辟结点的坐标
}
//11.1、向备用链表的首结点前插入游标为k的结点
void Free(SLinkList L, int k)
{ // 将下标为k的空闲结点回收到备用链表(成为备用链表的第一个结点)
L[k].cur = L[0].cur; // 回收结点的"游标"指向备用链表的第一个结点
L[0].cur = k; // 备用链表的头结点指向新回收的结点
}
//11、删除第i个元素,并返回删除结点的值
bool ListDelete(SLinkList L, int i, ElemType &e)
{ // 删除在L中第i个数据元素e,并返回其值
int j, k = MAX_SIZE - 1; // k指向表头
if (i<1 || i>ListLength(L))//排除不在链表中的元素
return false;
for (j = 1; j < i; j++) // 移动i-1个元素
k = L[k].cur;//找到要删除节点元素的前一个结点
j = L[k].cur;//游标j指向第i个节点
L[k].cur = L[j].cur;//第i-1个节点的游标指向第i+1个节点
e = L[j].data;//获取到要删除的节点i的值
Free(L, j);//将要删除的结点给插入到备用链表的第一个结点前
return true;
}