本篇是静态链表的C语言实现,实现书中算法2.13-2.17。
将数组中的一个分量表示结点,同时用游标代替指针指示结点在数组中的相对位置的,用数组来描述的链表叫做静态链表。
对于静态链表我们应注意:数组的第一个和最后一个元素做特殊处理,他们的data域不存放数据;通常把未使用的数组元素及删除的数组元素称为备用链表;数组的第一个元素,下标为0的那个元素的cur存放的就是备用链表的第一个节点的下标;数组的最后一个元素,下表为MAXSIZE-1的cur存放第一个有数值元素的下标,相当于单链表的头几点的作用;当前数组中的最后一元素(不一定为MAXSIZE-1)的游标为0.
主要实现例2-3——集合运算(A-B)U(B-A)来讨论静态链表的算法
declaration.h
#ifndef DECLARATION_H_INCLUDED
#define DECLARATION_H_INCLUDED
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
#define MAXSIZE 100
#define ElemType char
typedef int Status;
typedef struct
{
ElemType data;
int cur;//游标(指示器cur)代替指针指示结点在数组中的相对位置
}component, SLinkList[MAXSIZE];
//借用一维数组描述线性链表,便于在不设“指针”类型的高级程序设计语言中使用链表结构
#endif // DECLARATION_H_INCLUDED
//数组的第0分量作头结点,其指针域指示链表的第一个结点。
//这种存储结构需要先分配一个较大的空间,但在作线性表的插入和删除时不需移动元素
//仅需修改指针,故仍具有链式存储结构的主要优点。
//若S为SLinkList型变量,第i个分量表示连表的第K个结点,则S[i].cur指示第K+1个结点的位置
//整型游标i代替指针q,i=S[i].cur的操作相当于执行p=p->next;
function.h
#ifndef FUNCTION_H_INCLUDED
#define FUNCTION_H_INCLUDED
Status LocateElem_SL(SLinkList S, ElemType e);
//在静态链表中查找e,若存在返回他的位序否则返回0
void InitSpace_SL(SLinkList space);
//将一维数组*space中各分量链成一个备用链表,(*space)[0].cur为头指针
Status Malloc_SL(SLinkList space);
//若备用链表非空,则返回分配的节点下标否则返回0
void Free_SL(SLinkList space, int k);
//将K为下标的空闲结点回收到备用链表
void Difference(SLinkList space, int S);
//依次输入集合A和B的元素,在一维数组space中建立表示集合(A-B)U(B-A)
//的静态链表,S为其头指针。假设备用空间足够大,(*space)[0].cur为其头指针
void Print_SL(SLinkList space);
#endif // FUNCTION_H_INCLUDED
function.c
#include <stdio.h>
#include "declaration.h"
Status LocateElem_SL(SLinkList S, ElemType e)
{
//在静态链表中查找e,若存在返回他的位序否则返回0
Status i=S[0].cur; //i指示表中第一个结点,S[0]是头结点
while(S[i].data != e && i)
{
i=S[i].cur;
}
return i;
}//LocateElem_SL
void InitSpace_SL(SLinkList space)
{
//将一维数组*space中各分量链成一个备用链表,(*space)[0].cur为头指针
Status i;
for(i=0; i<MAXSIZE; i++)
space[i].cur=i+1;
space[MAXSIZE -1].cur=0; //当前数组中最后元素的游标为0
}//InitSpace_SL
Status Malloc_SL(SLinkList space)
{
//若备用链表非空,则返回分配的节点下标否则返回0
Status i=space[0].cur;
if( space[0].cur )
space[0].cur=space[i].cur;
return i;//i为备用链表中的第一个结点的下标,初始为1(此时整个链表空间都是空间的)
}
void Free_SL(SLinkList space, int k)
{
//空表中插入数据时会调用Malloc_SL函数,将space[0].cur指向备用链表的第一个结点
//将K为下标的空闲结点回收到备用链表
space[k].cur=space[0].cur;
space[0].cur=k;
}//Free_SL
void Difference(SLinkList space, int S)
{
//依次输入集合A和B的元素,在一维数组*space中建立表示集合(A-B)U(B-A)
//的静态链表,S为其头指针。假设备用空间足够大,(*space)[0].cur为其头指针
Status m,n,i,j,p;
ElemType b;
InitSpace_SL(space); //初始化备用空间
S=Malloc_SL(space); //生成S的头结点,S=1
Status r=S; //r指向S的当前最后结点
printf("please enter m,n as the size of A and B:");
scanf("%d%d",&m, &n);
getchar();//读取多余的换行符
for(j=1; j<=m ;j++)
{
i=Malloc_SL(space); //循环内第一次调用时返回2
scanf("%c", &(space[i].data));//从下标2的位置开始复制
space[r].cur=i; r=i; //插入到表尾
}//for,此时下标0位置的游标被置为8
space[r].cur=0; // 将A链表尾结点的指针置为空,r=7
for(j=1 ;j<=n;j++)
{
//为方便理解,以下注释都是针对第一次循环时情况的具体分析
//依次输入B的元素,若不在当前表中则插入,否则删除
scanf("%c",&b);
p=S;//p=1
int k=space[S].cur;//k指向集合A中的第一个结点,即下标为2的c
while( k != space[r].cur && space[k].data != b) //k != 0
{
p=k; k=space[k].cur; //p来记载被操作元素的前一个位置的下标
}
if( k == space[r].cur) //space[r].cur=0,即A链表中不存在读入的变量b。
{
i = Malloc_SL(space);//i=8;
space[i].data = b;//插入'a'
space[i].cur = space[r].cur;//space[8].cur=0;
space[r].cur=i;//space[7].cur=8
}
else
{
//该元素已在表中
space[p].cur = space[k].cur;
Free_SL(space, k);
if(r == k) r = p; //若删除的是r所指结点则需要修改尾指针
}//else
}//for
Print_SL(space);
}//difference
void Print_SL(SLinkList space)
{
Status i=2;
//根据游标间的关系打印(A-B)U(B-A)的结果
while( space[i].cur <11)
{
printf("%c",space[i].data);
i=space[i].cur;
}
}
main.c
#include <stdio.h>
#include <stdlib.h>
#include "declaration.h"
#include "function.h"
int main()
{
SLinkList space;
int S=0;
Difference(space, S);
return 0;
}
运行结果如下: