网易云课堂小甲鱼课程链接:数据结构与算法
一、什么是静态链表
C语言的伟大,在于指针的灵活性,使得它可以非常容易的操作内存中的地址和数据,这比其他高级语言更加灵活方便。(面向对象使用对象引用机制间接地实现了指针的某些功能)
但是在没有指针的时候,用数组代替指针来描述单链表。
定义:用数组描述的链表叫做静态链表,这种描述方法叫做游标实现法。
代码实现:
#include <stdio.h>
#define MAXSIZE 1000
typedef int ElemType; /* 定义数据元素类型,类型名为ElemType,此处所定义的数据元素只包含一个int型的数据项*/
typedef struct {
ElemType data; // 数据
int cur; // 游标(Cursor)
}Component, StaticLinkList[MAXSIZE];
特点
- 静态链表中,每一个结点包含两个部分:data(数据)和cur(存储该元素下一个元素所在数组中对应的下标)
- 下标为0的结点不包含任何数据:如图中,下标为0的结点它的cur存储的是备用链表(即没有存储数据的那些结点,图中下标5后面的结点)。
- 数组的最后一个元素的cur存放第一个元素的下标:如图中最后一个元素的cur是1,存放的是静态链表的第一个结点的下标,注意此处的第一个结点,是指有实质意义的数据的结点,如图中的数据A存放在下标为1的结点,那么1就是第一个结点。
- 链表的最后一个元素存cur存放0:链表的最后一个元素,并不一定是数组的最后一个元素(如图中下标为4的元素),它的cur一般是存放0,表示它后面的结点为空了。
对静态链表进行初始化
对静态链表进行初始化相当于初始化数组操作
// 1.对静态链表进行初始化 - 相当于初始化数组
Status InitList(StaticLinkList space){
int i;
for (i = 0; i < MAXSIZE-1; i++)
space[i].cur = i+1;
space[MAXSIZE-1].cur = 0;
return OK;
}
获得链表的长度
// 获得链表的长度
int ListLength(StaticLinkList L){
int j = 0;
int i = L[MAXSIZE-1].cur;
while (i) {
i = L[i].cur;
j++;
}
return j;
}
二、静态链表的插入操作
在动态链表中,结点的申请和释放分别借用C语言的
malloc()
和free()
函数来实现。
在静态链表中,操作的是数组,不存在动态链表的节点申请和释放的问题,所以需要自己实现这两个函数,才可以做到插入和删除操作。
为了辨明数组中哪些分量未被使用,解决办法是将所有未被使用的以及已被删除的分量用游标链成一个备用的链表。
在上图的A后面插入一个B的的方式如图:
代码实现:
// 2.静态链表的插入操作
// 2.1获取空闲分量的下标
int Malloc_SLL(StaticLinkList space){
int i = space[0].cur;
if (space[0].cur)
space[0].cur = space[i].cur;
return i;
}
// 2.2在静态链表L中第i个元素之前插入新的数据元素e
Status ListInsert(StaticLinkList L, int i, ElemType e){
int j,k,l;
k = MAXSIZE - 1; // 数组的最后一个元素
if (i<1 || i>ListLength(L)+1) return ERROR;
j = Malloc_SLL(L); // 得到备用链表的第一个元素
if (j) { // 如果元素存在
L[j].data = e;
for (l = 1; l<=i-1; l++) { // 得到i-1个元素的下标
k = L[k].cur;
}
L[j].cur = L[k].cur; // 将第i-1个元素的cur设置为新加的这个结点的下标,将新加的这个结点的下标设置为之前第i-1个元素存储的cur值
L[k].cur = j;
return OK;
}
return ERROR;
}
静态链表的插入需要分两步,先是获取空闲的分量,在进行插入操作。
三、静态链表的删除操作
删除的元素要放到备用链表里面
代码实现:
// 3.静态链表的删除操作
// 3.1删除在L中的第i个元素
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++)
k = L[k].cur;
j = L[k].cur;
L[k].cur = L[j].cur;
Free_SLL(L, j);
return OK;
}
// 3.2将下标为k的空闲结点回收到备用链表
void Free_SLL(StaticLinkList space, int k){
space[k].cur = space[0].cur;
space[0].cur = k;
}
静态链表的优缺点
优点:
- 在插入和删除操作时,只需要修改游标,不需要移动元素,从而改进了在顺序存储结构中的插入和删除操作需要移动大量元素的缺点。
缺点:
- 没有解决连续存储分配(数组)带来的表长难以确定的问题。
- 失去了顺序存储结构随机存取的特性。