这篇博文旨在记录“静态链表”这个知识点的,还是写得比较通俗易懂的。本文实现代码是参考了这位老兄的,在此表示衷心感谢,参考了这篇博文,我对于静态链表才有了比较清晰的理解。
静态链表是什么?
(1)静态链表中的链表,表明它是一种链表,其增删查改等操作基本还是与我们普通链表(单链表)并无二致的。
(2)静态链表中的静态,是指它本质是个数组,不像我们普通链表那样随时可以扩充长度,它有个最大长度限制。另外,操作它并不是依靠指针的,而是依靠一种叫“游标”的东西,但这个游标的作用就相当于指针的作用,也可以理解为“索引值”。
(3)静态链表的头跟尾:头就是数组的第一个元素(包括数据(data)区和游标(cursor)区),不过这个元素数据区不填入有效数据(当然也可以与我们链表头结点的附加功能那样用来记录静态链表的长度之类的),而游标区保存第一个尚未保存有效数据的元素游标值;
尾就是最后一个有效数据啦,它的数据区还是有有效数据的,而它的游标区就是零了。当然,这个数组(从数组的角度看)的它的最后一个元素也有特殊意义,它的 数据区并不保存有效数据,而游标区保存的是链表中第一个有效数据的游标值。
(4)静态链表也有已使用链表和备用链表之分,具体的理解可以看上面的老兄的博文。不过我并不想把静态链表想得太复杂,所以就没有写这方面的了。
为什么需要静态链表?
(1),大部分说法是:让没有指针的高级语言可以享受到链表的好处(不过这点和我们C语言没啥关系)。
(2)其他说法有:有说可以节约频繁的分配/删除内存的时间,有说在内存分配和使用上更安全等。
怎么使用静态链表?
(1)声明数据类型
//例如
typedef char elemtype;
typedef struct {
int cur;
elemtype data;
}snode_t,slist_t;
(2)定义静态链表(也就是定义数组,也就是分配一块空间啦)
(3)增删查改等常规操作,也普通链表没啥区别。
源代码示例和实验现象分析
(现象自己对着分析就可以了)
源代码
#include <stdio.h>
#define MAXSIZE 10
typedef char elemtype;
typedef struct {
int cur;
elemtype data;
}snode_t,slist_t;
slist_t *slist_init(slist_t *p);
int slist_malloc(slist_t *p);
void slist_free(slist_t *p,int j);
int slist_listlen(slist_t *p);
void slist_insert(slist_t *p,int i,elemtype e);
void slist_delete(slist_t *p,int i);
int main(int argc,char *argv[])
{
int i;
snode_t sl_node[MAXSIZE];
slist_t *sl = sl_node;
slist_init(sl);
for(i=1;i<8;i++){
slist_insert(sl,i,'a'+i-1);
}
slist_delete(sl,3);
slist_insert(sl,4,'m');
for(i=0;i<MAXSIZE;i++){
printf("sl[%d] %c %d\n",i,sl[i].data,sl[i].cur);
}
return 0;
}
slist_t *slist_init(slist_t *p)
{
int i;
for(i=0;i<MAXSIZE-1;i++){
p[i].cur=i+1;
}
p[MAXSIZE-1].cur=0;
return p;
}
int slist_malloc(slist_t *p)
{
int i = p[0].cur;
if(i){
p[0].cur = p[i].cur;//更新首元素游标
}
return i;
}
void slist_free(slist_t *p,int j)
{
p[j].cur = p[0].cur;
p[0].cur = j;
}
int slist_listlen(slist_t *p)
{
int i = p[MAXSIZE -1].cur;
int j=0;
while(i){
j++;
i=p[i].cur;
}
return j;
}
void slist_insert(slist_t *p,int i,elemtype e)
{
if(i<1 || i>slist_listlen(p)+1){
printf("插入位置不合理\n");
return ;
}
int k = MAXSIZE -1;
int j = slist_malloc(p);//获取备用下标
int index;
if(j){
p[j].data = e;
for(index=1;index<i;index++){ //在第i个下标前一个位置
k = p[k].cur;
}
p[j].cur=p[k].cur;//新插入的元素游标指向第i个下标的位置
p[k].cur=j;
}
}
void slist_delete(slist_t *p,int i)
{
if(i<1||i>slist_listlen(p)){
printf("删除位置不合理\n");
return;
}
int k = MAXSIZE -1;
int index;
for(index=1;index<i;index++){
k=p[k].cur;
}
int j=p[k].cur;//这里就是欲删除结点的游标了
p[k].cur=p[j].cur;
slist_free(p,i);
}
现象
[root@localhost hello]# ./staticlist
sl[0] 8
sl[1] a 2
sl[2] b 4
sl[3] m 5
sl[4] d 3
sl[5] e 6
sl[6] f 7
sl[7] g 0
sl[8] 9
sl[9] 1
[root@localhost hello]#
在这里总结一下
(1)静态链表(数组)第一个元素和最后一个元素数据区一般并不存储有效数据,而其游标区有特殊含义。第一个元素的游标区“指向”第一个尚未被使用的元素(这个怎么定义第一个呢?这里确实要看上面老兄博文中的定义了,可以去看看);最后一个元素的游标区“指向”我们已经使用的链表中的第一个元素,也就是指向第一个结点。
(2)静态链表的尾(我这里说的并不是数组中最后一个元素,而是指最后一个结点)的游标区为0。
(3)静态链表的第一个元素的游标区如果是0,则表示这个静态链表已经满了。