静态单链表存储类型
某些高级语言如 BASIC,没有“指针”数据类型,但有“结构体”数据类型。在这类
高级语言中,若要使用链表结构,就需先开辟一个充分大的结构体数组。结构体的一个成员存放数据,另一个成员(“游标”)存放下一个数的位置(下标),这称为静态链表。输出链表不是按数组的下标顺序输出的,而是由一个指定的位置开始根据游标依次输出的。
component为结构体,含有数据成员和下一个元素的下标。LinkList为数组名称。
#define MAX_SIZE 100 // 链表的最大长度
typedef int ElemType;
typedef struct {
ElemType data;
int cur; //“游标”,下一个数的下标
}component,LinkList[MAX_SIZE];
静态数组有 2 个链表,一个链表上链接的是线性表的结点,另一个链表(备用链表)上链接的是所有没被使用的结点。
将所有空闲结点链接形成一个备用链表,数组下标为 0 的单元为备用链表的头结点。当线性表需要新结点时,把备用链表中的首元结点(由[0].cur 指示)从备用链表中删除,作为新结点,插入线性表。
当删除线性表中的结点时,被删除的结点插入备用链表中,成为备用链表的首元结点。
从备用链表删除结点或向备用链表插入结点都在表头进行,因为这样效率最高。
生成静态链表的方法可有两种:一种是在一个数组中只生成一个静态链表,这种情况可以固定静态链表的头指针位置,如最后一个单元([MAX_SIZE-1]);另一种是在一个数组中可根据需要生成若干个独立的链表,每个链表的头指针在生成链表时才指定。
注意:
所有的操作函数都不用&L,直接用L,因为L为数组的基地址,所有函数都没有改变。
开辟结点
若备用链表非空,则返回分配的结点下标(备用链表的第一个结点);否则返回0
int Malloc(LinkList space) {
int i = space[0].cur;
if (i) // 备用链表非空
space[0].cur = space[i].cur;// 备用链表的头结点指向原备用链表的第二个结点
return i; // 返回新开辟结点的坐标
}
释放结点
将下标为k的空闲结点回收到备用链表(成为备用链表的第一个结点)。
void Free(LinkList space,int k) {
space[k].cur=space[0].cur; // 回收结点的"游标"指向备用链表的第一个结点
space[0].cur = k; // 备用链表的头结点指向新回收的结点
}
基本操作
1.初始化
构造一个空的链表L,表头为L的最后一个单元L[MAX_SIZE-1],其余单元链成一个备用链表,表头为L的第一个单元L[0],“0”表示空指针。
void InitList(LinkList L) {
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.销毁、清空
将L重置为空表;i为链表的首结点,k为链表的首结点,j为链表的尾结点。就是将里链表的首结点连接到备用结点的头结点后,再将原来备用链表的首结点连接到链表的尾结点后;将链表的头结点置空。
void ClearList(LinkList L) {
int i, j, k;
i = L[MAX_SIZE - 1].cur; // 链表首结点的位置
L[MAX_SIZE - 1].cur = 0; // 链表空
k = L[0].cur; // 备用链表首结点的位置
L[0].cur = i; // 把链表的首结点连到备用链表的头结点后
while (i) { // 遍历到链表尾,j用来保存链表尾结点的位置
j = i;
i = L[i].cur;
}
L[j].cur = k; // 备用链表的首结点接到链表的尾结点后
}
3.为空
bool ListEmpty(LinkList L) {
if (L[MAX_SIZE - 1].cur == 0) // 空表
return true;
else
return false;
}
4.长度
int ListLength(LinkList L) {
int j = 0, i = L[MAX_SIZE - 1].cur;
while (i) {
i = L[i].cur;
++j;
}
return j;
}
5.获取元素
返回L中第i个元素的值
bool GetElem(LinkList L, int i, ElemType& e)
{
int k = MAX_SIZE - 1; // k指向表头序号
if (i<1 || i>ListLength(L)) //i值不合法
return false;
for (int j = 1; j <= i; j++) // 移动到第i个元素处
k = L[k].cur;
e = L[k].data;
return true;
}
6.定位元素
在静态单链线性表L中查找第1个值为e的元素。若找到,则返回它在L中的位序;否则返回0
int LocateElem(LinkList L, ElemType e)
{
int i = L[MAX_SIZE - 1].cur; // i指示表中第一个结点
while (i && L[i].data != e) // 在表中顺链查找
i = L[i].cur;
return i;
}
7.前驱
若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱; 否则操作失败,pre_e无定义
bool PriorElem(LinkList L, ElemType cur_e, ElemType& pre_e)
{
int j, i = L[MAX_SIZE - 1].cur; // i指示链表第一个结点的位置
do{ // 向后移动结点
j = i;
i = L[i].cur;
} while (i && cur_e != L[i].data);
if (i) // 找到该元素
{
pre_e = L[j].data;
return true;
}
return false;
}
8.后继
若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后继;否则操作失败,next_e无定义。用LocateElem函数来找到值为cur_e的元素的位置,如果cur_e不存在或者无后继,则返回错误。
bool NextElem(LinkList L, ElemType cur_e, ElemType& next_e)
{
int j, i = LocateElem(L, cur_e); // 在L中查找第一个值为cur_e的元素的位置
if (i) // L中存在元素cur_e
{
j = L[i].cur; // cur_e的后继的位置
if (j) // cur_e有后继
{
next_e = L[j].data;
return true; // cur_e元素有后继
}
}
return false; // L不存在cur_e元素,cur_e元素无后继
}
9.插入
在L中第i个元素之前插入新的数据元素e。注意i的范围1<=i<length+1。l与k同步。
bool ListInsert(LinkList L, int i, ElemType e) {
if (i<1 || i>ListLength(L) + 1)
return false;
int k = MAX_SIZE - 1; // k指向头结点
int j = Malloc(L); // 申请新单元
if (j) { // 申请成功
L[j].data = e; // 赋值给新单元
for (int l = 0; l < i-1; l++) // 移动到第i-1个结点,k为第i-1个结点的位置
k = L[k].cur;
L[j].cur = L[k].cur;
L[k].cur = j;
return true;
}
return false; //申请失败
}
10.删除
删除在L中第i个数据元素e,并返回其值。注意i的范围1<=i<length。j与k同步。
bool ListDelete(LinkList L, int i, ElemType& e) {
if (i<1 || i>ListLength(L))
return false;
int k = MAX_SIZE - 1, j; // k指向头结点,j用来计数
for (j = 0; j < i-1; j++) // 移动到第i-1个结点,k为第i-1个结点的位置
k = L[k].cur;
j = L[k].cur; //j用来存放释放元素位置
L[k].cur = L[j].cur;
e = L[j].data;
Free(L, j);
return true;
}
11.遍历
void ListTraverse(LinkList L,void (* vi)(ElemType n)) {
int i = L[MAX_SIZE - 1].cur; // 指向第一个元素
while (i) { // 没到静态链表尾
vi(L[i].data);
i=L[i].cur; // 指向下一个元素
}
printf("\n");
}
测试函数
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
void visit(ElemType n) {
printf("%d ", n);
}
int main() {
int i,j,k,e;
LinkList L;
InitList(L); //初始化
for (int j = 1; j <= 5; j++)
ListInsert(L, 1, j); //插入
printf("在L的表头依次插入1~5后:L=");
ListTraverse(L, visit);
printf("L是否空:i=%d(1:是 0:否)表L的长度=%d\n", ListEmpty(L), ListLength(L));
ClearList(L);
printf("清空L后:L=");
ListTraverse(L, visit);
printf("L是否空:i=%d(1:是 0:否)表L的长度=%d\n", ListEmpty(L), ListLength(L));
for (int j = 1; j <= 10; j++)
ListInsert(L, j, j); //插入
printf("在L的表尾依次插入1~10后:L=");
ListTraverse(L, visit);
if (GetElem(L, 5, e)) //获取元素
printf("第5个元素的值为%d\n", e);
else
printf("没有第5个元素\n");
if (GetElem(L, 13, e))
printf("第13个元素的值为%d\n", e);
else
printf("没有第13个元素\n");
if (e = LocateElem(L, 8)) //定位元素
printf("元素8的位置为%d\n", e);
else
printf("没有值为8的元素\n");
if (e = LocateElem(L, 11))
printf("元素11的位置为%d\n", e);
else
printf("没有值为11的元素\n");
if (PriorElem(L, 10, e)) //前驱
printf("元素10的前驱为%d\n", e);
else
printf("元素10无前驱\n");
if (NextElem(L, 10, e))
printf("元素10的后继为%d\n", e);
else
printf("元素10无后继\n");
if (PriorElem(L, 1, e)) //后继
printf("元素1的前驱为%d\n", e);
else
printf("元素1无前驱\n");
if (NextElem(L, 1, e))
printf("元素1的后继为%d\n", e);
else
printf("元素1无后继\n");
ListTraverse(L, visit);
printf("表长为%d\n", ListLength(L));
if (ListDelete(L, 4, e)) //删除
printf("第4个元素为%d,已删除。\n", e);
else
printf("删除第4个元素失败(不存在此元素)。\n");
printf("依次输出L的元素:");
ListTraverse(L, visit);
return 0;
}
测试结果