一、静态链表
用指针操作链表的实现固然方便,有时候我们也可以用一维数组来实现链表的存储与操作,这种方法不用设立指针,对于在没有指针类型的高级程序设计语言中使用链表结构,可以用到。那么如何用数组来实现链表的存储?先来看它的存储结构:
#include <stdio.h>
#define MaxSize 100 /*链表的最大长度*/
#define ElemType int
/*静态单链表的存储结构*/
typedef struct Node {
ElemType data;
int cur; /*游标代替指针指示结点在数组中的相对位置*/
}component, SLinkList[MaxSize];
数组的每一个分量代表链表中的一个结点,分量结构中的游标指示器cur指示该结点在数组中的相对位置(例如cur等于5代表该节点在数组中下标为5的位置)。数组的第0个分量可以看成是链表的头结点,头结点的指针域cur指示的是链表中的首元结点。
由于数组方式是实现的链表结构要事先分配好空间大小,为了和指针型的链表区别开来,所以把这种用数组表示的链表成为静态链表。
假设space是SLinkList型的变量,则space[ 0 ].cur表示的是首元结点在数组中的位置,若令i=space[ 0 ].cur,则space[ i ].data就是首元结点的数据元素值。space[ i ].cur指示的是首元结点之后第二个结点在数组中的位置。若第N个数组分量表示的是第K个结点,那么space[ N ].cur表示的是第K+1个结点的位置。所以静态链表中的操作和指针型链表的操作相似。i=space[ i ].cur的操作其实就是指针型链表中的p=p->Next(后移操作)。例如如果我们想写出静态链表中的查找元素操作,可以这样写:
int Search_SL(SLinkList space, ElemType K, int S)
{
int i;
i=space[S].cur; /*i从链表的头结点开始找起*/
while (i && space[i].data!=K) {
i=space[i].cur; /*i指向下一个结点,相当于p=p->Next*/
}
if (space[i].data==K) {
printf("找到了,下标为:%d", i);
} else {
printf("没找到这个元素.");
}
return i;
}
让i从链表的头结点开始,如果i && space[i].data!=K的话,就让i=space[ i ].cur即指向下一个结点,直到找到元素位置,就返回该元素在数组中的位置下标。
与指针型链表不同的是,静态链表的malloc函数和free函数需要自己实现。malloc函数中将所有未被使用过的数组分量链接成一个链表,每次调用malloc函数时就返回一个分量出去。而free函数将释放的结点放回space链表中。下面我们先来看初始化链表怎么做:
/*初始化链表*/
void InitSpace(SLinkList space)
{
/*将一维数组中各个分量间建立关系链成一个链表,space[0].cur为头指针*/
int i;
for (i=0; i<MaxSize-1; i++) {
space[i].cur=i+1;
space[i].data=-1;
}
space[MaxSize-1].cur=0; /*链表中最后一个结点的"Next"指向NULL*/
}
将一维数组space中各个分量通过游标指示器cur建立链接关系形成一个链表,其中space[ 0 ].cur是头指针。
/*malloc函数*/
int Malloc_SL(SLinkList space)
{
/*模拟malloc函数,分配一个结点的空间*/
/*若链表非空,就返回分配的结点的下标,否则返回0*/
int i;
i=space[0].cur;
if (space[0].cur) {
space[0].cur=space[i].cur;/*指向下一个待分配的结点的下标*/
}
return i;
}
在链表中从第一个结点的位置开始,分出分量,返回该分量的位置下标i,把i的下一个结点赋给space[ 0 ].cur做下一次分配出去的结点用。
/*free函数*/
void free_SL(SLinkList space, int K)
{
/*将下标为K的空闲结点回收到备用链表*/
/*下标为K的空闲结点回收到space[0].cur*/
/*下一次调用malloc是就会先分配 K */
space[K].cur=space[0].cur;
space[0].cur=K;
}
free函数把要释放的下标为K的结点,让它的指针域cur等于space[ 0 ].cur,这样下一次调用malloc函数时就会把下标为K的结点分配出去。
完整代码在个人代码云:https://gitee.com/justinzeng/codes/783pvt9k5q1dnjl0ybxui80
- 循环链表
循环链表是链式存储结构的另一种形式,特点是循环链表中最后一个结点的指针域不是指向NULL而是指向链表的头结点,使整个链表形成一个环状,所以,从表中任何一个结点出发都可以找到其他结点。
循环链表的操作和线性链表的操作基本相同,不同的有例如遍历循环链表的时,循环条件不是p或p->Next==NULL; 而是判断p是否等于头结点:
循环链表首尾链接方式:
/*链接末尾结点指向头结点形成循环链表*/
void ConnectEndNode(List Ptrl)
{
List Ptrl1;
Ptrl1=Ptrl;
if (Ptrl1) {
while (Ptrl1->Next) {
Ptrl1=Ptrl1->Next;
}
Ptrl1->Next=Ptrl;
}
}
求表长:
/*遍历求表长*/
int Length(List Ptrl)
{
int count=0;
List P=Ptrl->Next;
while (P!=Ptrl) {
P=P->Next;
count++;
}
return count;
}
完整代码在个人代码云:https://gitee.com/justinzeng/codes/8ty9b4ez2rlh3swj0vciq30