静态链表的定义
定义数组元素都是由两个数据域组成的,data和cur。也就是说数组的每个下标都对应着一个data 和一个 cur。数据域 data ,用来存放数据元素,cur 相当于单链表中的 next 指针,用来存放后继元素的数组下标,我们将 cur 叫做游标。将这种用数组描述的链表叫静态链表,也叫游标实现法。
游标:
1、静态链表中,游标 cur 用于存放下一个元素在数组中的索引。它相当于单链表中的 next 指针,表示链表的下一个结点在数组中的位置。
2、未被使用的数组元素通常被组成一个备用链表。这个备用链表的头结点通常是数组的第一个元素。每个备用链表结点的 cur 域存储下一个备用结点在数组中的索引。
3、通常使用一个数组来模拟链表,其中数组的第一个元素用于表示备用链表的头结点,而数组的最后一个元素用于表示静态链表的头结点。
4、对于存有数据的链表,链表的尾节点游标(cur)通常被设置为 0,以表示链表的结束。这是因为链表的尾节点后面没有下一个结点,所以 cur 设置为 0 表示链表的结束。
5、 对于备用链表,通常不需要特别设置尾节点游标,因为备用链表是通过数组中未被使用的元素构成的,游标为 0 通常被视为备用链表的结束。

如果不理解的话可以看这篇文章:【数据结构与算法】静态链表 - 简书 (jianshu.com)
代码实现:
1.宏定义
#define OK 1
#define ERROR 0
#define MAXSIZE 1000
MAXSIZE定义了数组长度
2.静态链表的数据结构
typedef struct {
int data;
int cur;
}Component,StaticLinkList[MAXSIZE];
3.链表初始化
根据静态链表的定义,将游标存放下一个元素的数组下标,将最后一个元素,即 L[MAXSIZE - 1] 的游标值设置为: 0 ,表示指向备用链表的头节点
/*链表初始化*/
int InitLinkList(StaticLinkList L)
{
for (int i = 0; i < MAXSIZE - 1; i++)
{
L[i].cur = i + 1;
}
L[MAXSIZE - 1].cur = 0;
return OK;
}
4.获取静态链表的长度
实现思路:从链表的头结点出发,通过头结点的 cur 域遍历整个链表,逐个统计链表中的结点数目。在判断链表是否为空的部分,通过检查头结点的 cur 是否为 0,可以快速判断链表是否为空。
(1)在静态链表的实现中,数组的第一个元素通常作为备用链表的头结点。如果头结点的 cur 值为 0,表示备用链表为空,即没有可用的备用结点。此时,函数返回错误代码 ERROR。
if (L[0].cur == 0) {
//链表为空
return ERROR;
}
(2)如果链表不为空,就进入循环计算链表的长度。从头结点的位置开始,通过头结点的 cur 域逐个访问链表中的结点,每访问一个结点,就将长度加一。这个循环会一直进行,直到 i 的值为 0,表示链表的末尾。
int length = 0;
int i = L[MAXSIZE - 1].cur; // 头结点的位置
while (i != 0) {
++length;
i = L[i].cur;
}
5.在指定位置插入元素。
(1)检查插入位置 i 是否有效,即应在范围 [1, ListLength(L) + 1] 内。如果无效,打印错误消息并返回错误代码 (ERROR)。
if (i<1 || i>ListLength(L) + 1) {
printf("插入位置非法!\n");
return ERROR;
}
(2)分配新节点:从备用链表中获取第一个节点的索引,并更新备用链表的头节点。
int newNode = L[0].cur; //获取备用链表的第一个节点的下标
L[0].cur = L[newNode].cur; //更新备用链表的头节点
(3)插入数据:将提供的数据插入新节点;查找前驱节点,定位到插入位置i 的前一个节点;更新指针,将新节点插入链表。
L[newNode].data = data; //插入数据
int j = MAXSIZE - 1; //头结点的位置
for (int k = 1; k < i; ++k)
{
//定位到前一个节点
j = L[j].cur;
}
L[newNode].cur = L[j].cur; //新节点指向后继节点
L[j].cur = newNode; //前驱节点指向新节点
这段代码通过初始化备用链表的第一个节点作为新节点插入到静态链表中,并更新备用链表的头节点实现在静态链表中的指定位置插入元素。
/*在指定位置插入元素*/
int ListInsert(StaticLinkList L, int i, int data)
{
if (i<1 || i>ListLength(L) + 1) {
printf("插入位置非法!\n");
return ERROR;
}
int newNode = L[0].cur; //获取备用链表的第一个节点的下标
L[0].cur = L[newNode].cur; //更新备用链表的头节点
L[newNode].data = data; //插入数据
int j = MAXSIZE - 1; //头结点的位置
for (int k = 1; k < i; ++k)
{
//定位到前一个节点
j = L[j].cur;
}
L[newNode].cur = L[j].cur; //新节点指向后继节点
L[j].cur = newNode; //前驱节点指向新节点
return OK;
}
6.删除指定位置的节点
(1)检查删除位置是否合法,与插入同理
(2)查找前驱节点,更新静态链表的游标,使用前驱节点的游标指向 需要删除的节点的游标(跳过需要删除的节点)
int j = MAXSIZE - 1; //头节点位置
for (int k = 1; k < i; ++k)
{
j = L[j].cur;
}
int deleteNode = L[j].cur; //待删除的节点
L[j].cur = L[deleteNode].cur; //待删除节点的前驱节点
(3)将需要删除的节点加入到备用链表
//将删除节点加入到备用链表
L[deleteNode].cur = L[0].cur;
L[0].cur = deleteNode;
这段代码通过静态链表跳过需要删除的节点,将需要删除的节点指向删除节点的下一个节点来达到删除的效果,并将删除的节点插入到备用链表中。实现了删除指定位置的节点。
/*删除指定位置的元素*/
int ListDelete(StaticLinkList L, int i)
{
if (i<1 || i>ListLength(L))
{
printf("插入位置非法!\n");
return ERROR;
}
int j = MAXSIZE - 1; //头节点位置
for (int k = 1; k < i; ++k)
{
j = L[j].cur;
}
int deleteNode = L[j].cur; //待删除的节点
L[j].cur = L[deleteNode].cur; //待删除节点的前驱节点
//将删除节点加入到备用链表
L[deleteNode].cur = L[0].cur;
L[0].cur = deleteNode;
return OK;
}
7.遍历静态链表,打印静态链表中的数据
/*打印静态链表*/
void PrintList(const StaticLinkList L)
{
int i = L[MAXSIZE - 1].cur;
while (i != 0)
{
printf("%d ", L[i].data);
i = L[i].cur;
}
printf("\n");
}
完整代码:
#include<stdio.h>
#define OK 1
#define ERROR 0
#define MAXSIZE 1000
//静态链表的数据结构
typedef struct {
int data;
int cur;
}Component,StaticLinkList[MAXSIZE];
/*链表初始化*/
int InitLinkList(StaticLinkList L)
{
for (int i = 0; i < MAXSIZE - 1; i++)
{
L[i].cur = i + 1;
}
L[MAXSIZE - 1].cur = 0;
return OK;
}
/*获取静态链表的长度*/
int ListLength(const StaticLinkList L)
{
if (L[0].cur == 0) {
//链表为空
return ERROR;
}
int length = 0;
int i = L[MAXSIZE - 1].cur; //头结点的位置
while (i != 0)
{
++length;
i = L[i].cur;
}
return length;
}
/*在指定位置插入元素*/
int ListInsert(StaticLinkList L, int i, int data)
{
if (i<1 || i>ListLength(L) + 1) {
printf("插入位置非法!\n");
return ERROR;
}
int newNode = L[0].cur; //获取备用链表的第一个节点的下标
L[0].cur = L[newNode].cur; //更新备用链表的头节点
L[newNode].data = data; //插入数据
int j = MAXSIZE - 1; //头结点的位置
for (int k = 1; k < i; ++k)
{
//定位到前一个节点
j = L[j].cur;
}
L[newNode].cur = L[j].cur; //新节点指向后继节点
L[j].cur = newNode; //前驱节点指向新节点
return OK;
}
/*删除指定位置的元素*/
int ListDelete(StaticLinkList L, int i)
{
if (i<1 || i>ListLength(L))
{
printf("插入位置非法!\n");
return ERROR;
}
int j = MAXSIZE - 1; //头节点位置
for (int k = 1; k < i; ++k)
{
j = L[j].cur;
}
int deleteNode = L[j].cur; //待删除的节点
L[j].cur = L[deleteNode].cur; //待删除节点的前驱节点
//将删除节点加入到备用链表
L[deleteNode].cur = L[0].cur;
L[0].cur = deleteNode;
return OK;
}
/*打印静态链表*/
void PrintList(const StaticLinkList L)
{
int i = L[MAXSIZE - 1].cur;
while (i != 0)
{
printf("%d ", L[i].data);
i = L[i].cur;
}
printf("\n");
}
int main() {
StaticLinkList space;
if (InitLinkList(space)) {
printf("初始化成功!\n");
}
else {
printf("初始化失败!\n");
}
// 插入元素
if (ListInsert(space, 1, 10)) {
printf("插入成功\n");
}
printf("链表长度:%d\n", ListLength(space));
if (ListInsert(space, 2, 20)) {
printf("插入成功\n");
}
printf("链表长度:%d\n", ListLength(space));
if (ListInsert(space, 3, 30)) {
printf("插入成功\n");
}
printf("链表长度:%d\n", ListLength(space));
//打印
printf("链表内容:");
PrintList(space);
// 获取链表长度
printf("链表长度:%d\n", ListLength(space));
// 删除元素
printf("删除链表第二个元素\n");
ListDelete(space, 2);
// 打印链表
printf("删除后的链表内容:");
PrintList(space);
return 0;
}

581

被折叠的 条评论
为什么被折叠?



