0:实现静态链表的方法
定义一个较大的结构数组作为备用结点空间(即存储池)。当申请结点时,每个结点应含有两个域:data域和cursor域。data域用来存放结点的数据信息,此时的cursor域不在是指针而是游标指示器,游标指示器指示其后继结点在结构数组中的相对位置(即数组下标)。
数组的第0个分量可以设计成表的头结点,头结点的next域指示了表中第一个结点的位置。表中当前最后一个结点的域为0,表示静态单链表的结束。我们把这种用游标指示器实现的单链表叫做静态单链表,static
linked list。
linked list。
静态单链表同样可以借助一维数组来描述。
有趣的是:程序自始至终用的都是同一个数组,那怎么来区分呢?解决方案是:把这个数组逻辑分成两个部分(在下面的代码中你可以看到),一部分叫做空闲节点区域,另一部分叫非空闲节点区域(笔者这么叫,你也可以叫别的名字,但是你要想象把这个数组分成两个部分就ok了),前面说了是“逻辑”分隔,那怎么维护这个数组呢?答案是靠cursor域链接下一个节点来维护的。比如看下图(图真难画啊……),那么怎么标注非空闲链表呢?答案是自己定义一个下标来记住非空闲链表头节点下标就ok了。
你的头脑中要有一个场景:一个数组逻辑分成两部分,他们都是通过指针来连接的,空闲链表由空闲头结点来链接起来,非空闲链表由非空闲头结点连接起来,空闲头结点就是数组下标为0的节点,由于每次申请都是从空闲头结点下一个位置申请空闲节点,所以导致非空闲头结点是数组下标为1的节点(这个你在下图或者程序中能够体会到)并且把这个下标记录下来,然后下面的工作就是维护这两个链表了
1:静态链表的实现
- 数据结构
#include <stdio.h> #define N 100 typedef struct{ char data; int cur;//游标域或者指针域 }SList;
数据结构很好理解,不在赘述。
- 初始化
void init_sl(SList slist[]){//初始化成空闲静态链表, int i; for(i=0;i<N-1;i++) { slist[i].cur=i+1; } slist[N-1].cur=0;//最后一个指针域指向0 }
- 申请分配一个空闲节点
int malloc_sl(SList slist[]){//分配空闲节点 int i=slist[0].cur;//总是取头结点之后的第一个空闲结点做分配,同时空闲链表非空,头结点做调整 if (i) { slist[0].cur=slist[i].cur;//空闲链表头结点调整指针域 } return i;//返回申请到的空闲节点的数组下标 }
注意:这里每次申请空闲节点都是把空闲链表的第一个节点(非头节点)返回,同理在释放空闲节点到空闲链表的的时候也是把空闲节点加到空闲链表头指针的后面第一个的位置
- 释放一个空闲节点到空闲链表
void free_sl(SList slist[],int k){//将k节点回收 slist[k].cur=slist[0].cur;//总是将回收的节点放在头结点之后 slist[0].cur=k; }
2:实例:求集合运算(A-B)U(B-A)的结果
int difference_sl(SList slist[],int n){ int i,m,q,p; char tmp[2];//为避免出现scanf输入字符出现接受回车符,则采用输入字符串的形式 int start,end;//start是哨兵,end指向最后的节点 init_sl(slist);//初始化 start=malloc_sl(slist);//从空闲链表中取出第一个空闲节点生成链表的头结点 //注意区分,现在是有一个空闲链表是slist,里面都是空闲节点,每次申请malloc_sl和free_sl都是操作的是slist //然后非空静态链表,即集合A的链表是由start这个下标指引一直向后走,end指向非空链表的尾节点, //而且你发现start基本上都是1,因为开始的时候slist都是空闲节点,而分配又都是从头结点slist[0]之后的第一个 //节点,即slist[1]开始分配,所以start=1 end=start; while (n--) { scanf("%s",tmp); i=malloc_sl(slist); slist[i].data=tmp[0]; slist[end].cur=i; end=i;//end指针后移 } slist[end].cur=0;//这个勿忘!尾节点的指针为空 //至此A集合输入完毕,然后处理B集合 scanf("%d",&m); while (m--) { scanf("%s",tmp); //从A集合中扫描,如果A中存在tmp,则在A中删除(free_sl),即A-B,如果没有则添加入A,即B-A q=start;//q是p的前驱 p=slist[start].cur; while (p!=slist[end].cur&&slist[p].data!=tmp[0]) { q=p; p=slist[p].cur; } if (p!=slist[end].cur)//说明在A中找到了tmp,则删除 { slist[q].cur=slist[p].cur;//跨过p节点 free_sl(slist,p); if (end==p) { end=q;//如果删除的是尾节点,则修改尾节点指针 } }else{ i=malloc_sl(slist); slist[i].data=tmp[0]; slist[i].cur=slist[end].cur; slist[end].cur=i;//插在end后,end位置不变,因为end是标志集合A的结束,但是这里你也可以变化end指针,就是加上end=i即可 } } return start; }
加上一个打印的函数
void print_sl(SList slist[],int start){ int p=slist[start].cur; while (p) { printf("%c ",slist[p].data); p=slist[p].cur; } printf("\n"); }
加上一个main函数
int main(){ int n,start; SList slist[N]; freopen("1.txt","r",stdin); //该程序是求(A-B)U(B-A)集合运算 while (scanf("%d",&n)==1) { start=difference_sl(slist,n); print_sl(slist,start); } fclose(stdin); return 0; }
3:下载代码
如果你想下载这些代码和测试数据请猛戳【去下载静态链表的C语言实现】程序对测试数据的输出是
c e g d n a