其实这篇文章不是什么重点内容,如果前面的链表理解了可以直接跳过不看。
当语言不直接或者间接提供指针的时候就需要使用静态数组了。其实最核心的思想就是用数组来代替指针,具体做法如下。
首先这里使用的数组是结构体数组,数组元素有两个元素域,data
和cur
,前者存放数据,后者相当于单链表中的next
指针,指示下一个地址或者说数组下标。
#define MAXSIZE 1000
typedef struct{
ElemType data;
int cur;
}Component, StaticLinkList[MAXSIZE];
声明出来的数组里面第0个和最后一个元素不保存数据,剩下的未被使用的数组元素称为备用链表。最后大概结构是这样的:
初始化代码:
#define MAXSIZE 1000
typedef struct{
ElemType data;
int cur;
}Component, StaticLinkList[MAXSIZE];
Status InitList(StaticLinkList space){
for(int i; i<MAXSIZE-1; i++){
space[i].cur = i+1; // 指向下一个结点
}
space[MAXSIZE-1].cur = 0; // 指向回头结点
return OK;
}
当存入数据的时候,最后一个元素的cur
指向第一个有元素的位置,例如下面的例子,最后一个元素的cur
为1。
最后面一个存有数据的元素中cur
指向第一个元素,即0
,而第0个元素cur
的值设置为7,因为从7开始时空闲空间。
首先,分配空间,因为第0个元素指向的是空闲的空间,所以第0个元素的cur
值应当为分配空间后的空闲元素,因此可以这样实现:
int Malloc_SLL(StaticLinkList space){
int i = space[0].cur; // 知道当前空闲的空间坐标
if(space[0].cur){
space[0].cur = space[i].cur; // 相当于让指向空闲空间的坐标往后移动一格,
// 这样空出来的位置就可以用来存放新的元素了
}
return i;
}
函数最后返回空闲位置的下标i
。
在空闲出来空间之后就可以开始插入操作了。首先回忆一下链表的插入操作是怎么样的,我们先让待插入元素的next
指针指向待插入位置的下一个元素,然后让当前位置的前一个元素指向待插入的元素。只是这里cur
代表了指针指向的地址,而在静态链表中地址就是数组下标。因此我们可以这样操作:
Status ListInsert(StaticLinkList L, int i, ElemType e){
int j, k;
k = MAXSIZE - 1; // 最后一个元素的下标
if(i < 1 || i > ListLength(L) + 1){
return ERROR;
}
j = Malloc_SLL(L); // 获取空闲位置的下标
if(j){
L[j].data = e;
for(int l = 1; l <= i-1; i++){
k = L[k].cur; // 遍历到第k-1个元素
}
L[j].cur = L[k].cur; // 先让待插入元素的next指针指向待插入位置的下一个元素
L[k].cur = j; // 让当前位置的前一个元素指向待插入的元素
return OK;
}
return ERROR;
}
最终效果有点像下面这张图:
删除元素需要对应的函数free()
,需要这样实现:
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; // 获取最后一个元素的cur值
L[k].cur = L[j].cur;// 让最后一个元素的cur值指向当前元素的下一个元素
Free_SSL(L, j); // 空出第j个位置
return OK;
}
void Free_SSL(StaticLinkList space, int k){
space[k].cur = space[0].cur;
space[0].cur = k;
}
上面的代码思路很简单,因为第0个元素的cur
存储的必然是空闲的位置,所以通过将要空闲出来的第k
个位置的cur
设置为第0个元素记录的空闲元素的位置,这样就可以将所有空闲元素串成一个“备用”链表,相当于就是将空闲位置连在一起了。然后负责存储第一个空闲位置的第0个元素更新cur
,让cur
指向最新的当前空闲的坐标。
int ListLength(StaticLinkList L){
int j = 0;
int i = L[MAXSIZE-1].cur; // 获取第一个有数据的元素的位置
while(i){ // 当i为0的时候说明已经遍历完所有有值元素,此时退出循环
i = L[i].cur;
j++;
}
return j;
}
最后一个元素的cur
指向的是第一个有数据的元素,而最后一个存着数据的元素的cur
指向第0个元素,且C语言中0就是false,因此可以通过这种方式来遍历静态链表,计算出链表长度。
其实如果从最后一个元素开始遍历静态链表,你会发现链表可以一直无缝从有值元素遍历到空闲元素,也就是说,结构恰好是个环:
然后说一下静态链表的优缺点:
《大话数据结构》