静态链表
静态链表:
1)线性存储结构的一种,兼顾顺序表和链表的优点,是顺序表和链表的升级;
2)静态链表的数据全部存储在数组中(顺序表),但存储的位置是随机的,数据直接的一对一关系是通过一个整型变量(称为“游标”,类似指针的功能)维持。
静态链表中的节点
数据域:用于存储数据元素的值
游标:即数组下标,表示直接后继元素所在数组中的位置
typedef struct
{
int data; //静态链表节点中的数据
int cur; //静态链表节点中的游标
}component;
例:
使用静态链表存储数据元素4、5、6,过程如下:
注:通常静态链表会将第一个数据元素放到数组下标为 1 (即 a[1] )的位置中。
图中从 a[1] 存储的数据元素 4 开始,通过存储的游标变量 3,可以在 a[3] 中找到元素 4 的直接后继元素 5;
通过元素 a[3] 存储的游标变量 6,可在在 a[6] 中找到元素 5 的直接后继元素 6;这样一直到某元素的游标变量为 0 截止。( a[0] 默认不存储数据元素)
最后一个元素的尾部游标始终指向 a[0]。
备用链表
静态链表中,除了数据本身通过游标组成链表外,还需要有一条连接各个空闲位置的链表,称为备用链表。
作用:
回收数组中未使用或者之前使用过(现在不用)的存储空间,留待后期使用。
即静态链表使用数组申请的物理空间中,存在两个链表,一条连接数据,另一条连接数组中为使用的空间。
注:通常备用链表的表头位于数组下标为 0( a[0] )的位置,而数据链表的表头位于数组下标为1( a[1] )的位置。
静态链表中设置备用链表的好处是,可以清楚地知道数组中是否有空闲位置,以便数据链表添加新数据时使用。比如,若静态链表中数组下标为 0 的位置上存有数据,则证明数组已满。
静态链表的实现
在数据链表未初始化之前,数组中所有位置都处于空闲状态,所以都链接在备用链表上。(图1)
向静态链表中添加数据时,需提前从备用链表中摘除结点,让新数据使用。
备用链表摘除节点最简单的方法是摘除 a[0] 的直接后继节点(即摘除 a[1] 的游标 2);
同样,向备用链表中添加空闲节点也是添加作为a[0]新的直接后继节点(图二中 a[1] 为 a[0] 新的直接后继结点(游标为 2)、图三中 a[2] 为 a[0] 新的直接后继结点(游标为 3)、图四中 a[3] 为 a[0] 新的直接后继结点(游标为 4))。
因为 a[0] 是备用链表的第一个节点,我们知道它的位置,操作它的直接后继节点相对容易,无需遍历备用链表,耗费的时间复杂度为 O(1)。
#include <stdio.h>
#include <stdlib.h>
#define maxSize 6
typedef struct
{
int data;
int cur; //游标
}component;
/*************************************************************************
创建备用链表
*****************************************************************************/
void reserveArr(component *array)
{
int i;
for(i = 0; i < maxSize; i++)
{
array[i].cur = i+1; //将每个数组分量链接到一起
}
array[maxSize-1].cur = 0; //链表最后一个节点的游标为0
}
/********************************************************
提取分配空间
若备用链表为非空,则返回分配的节点下标,否则返回0(当分配最后一个节点时,该节点的游标值为0)
********************************************************/
int mallocArr(component *array)
{
int i = array[0].cur;
if(array[0].cur)
{
//array[0].data=0;
array[0].cur = array[i].cur;
}
return i;
}
/***************************************************************************
初始化静态链表
****************************************************************************/
int initArr(component *array)
{
int body, tempBody, i, j;
reserveArr(array);
body = mallocArr(array);
tempBody = body; //声明一个变量把它当指针使,指向链表的最后一个节点,因为链表为空,所以和头结点重合
for( i=1; i<4; i++)
{
j = mallocArr(array); //从备用链表中拿出空闲的分量
array[tempBody].cur = j;//将申请的空闲分量链接在链表的最后一个结点后面
array[j].data = i; //给新申请的分量数据域初始化
tempBody = j; //将指向链表最后一个结点的指针后移
}
array[tempBody].cur = 0; //新的链表最后一个结点的指针设为0
return body;
}
void displayArr(component *array, int body)
{
int tempBody = body; //tempBody准备遍历使用
while(array[tempBody].cur)
{
printf("%d, %d\n", array[tempBody].data, array[tempBody].cur);
tempBody = array[tempBody].cur;
}
printf("%d, %d\n", array[tempBody].data, array[tempBody].cur);
}
int main()
{
int body;
component array[maxSize];
body = initArr(array);
printf("static link:\n");
displayArr(array,body);
system("pause");
return 0;
}
在静态链表中添加一个元素
void Insert(component *array,int body,int add,int a)//body链表头结点在数组中的位置,add插入元素的位置,a插入的元素
{
int tempBody = body;
int i,insert;
for(i = 1; i < add; i++)
{
tempBody = array[tempBody].cur;
}
insert = mallocArr(array);
array[insert].data = a;
array[insert].cur = array[tempBody].cur;
array[tempBody].cur = insert;
}
静态链表删除元素
1)将存有目标元素的节点从数据链表中摘除;
2)将摘除节点添加到备用链表,以便下次再用。
//备用链表回收空间的函数,其中array为存储数据的数组,k表示未使用节点所在数组的下标
void freeArr(component * array,int k){
array[k].cur = array[0].cur;
array[0].cur = k;
}
//删除结点函数,a 表示被删除结点中数据域存放的数据
void deletArr(component * array,int body,char a){
int tempBody = body;
//找到被删除结点的位置
while (array[tempBody].data !=a ) {
tempBody = array[tempBody].cur;
//当tempBody为0时,表示链表遍历结束,说明链表中没有存储该数据的结点
if (tempBody == 0) {
printf("链表中没有此数据");
return;
}
}
//运行到此,证明有该结点
int del = tempBody;
tempBody = body;
//找到该结点的上一个结点,做删除操作
while (array[tempBody].cur != del) {
tempBody =a rray[tempBody].cur;
}
//将被删除结点的游标直接给被删除结点的上一个结点
array[tempBody].cur = array[del].cur;
//回收被摘除节点的空间
freeArr(array, del);
}
静态链表中查找元素
/********************************************************
在以body作为头结点的链表中查找数据域为elem的结点在数组中的位置
*****************************************************/
int SelectElem(component * array,int body,char elem){
int tempBody = body;
while (array[tempBody].cur != 0) //当游标值为0时,表示链表结束
{
if (array[tempBody].data == elem) {
return tempBody;
}
tempBody = array[tempBody].cur;
}
return -1;//返回-1,表示在链表中没有找到该元素
}
静态链表中更改数据
/********************************************************
在以body作为头结点的链表中将数据域为oldElem的结点,数据域改为newElem
**********************************************************/
void amendElem(component * array, int body, char oldElem, char newElem)
{
int add = selectElem(array, body, oldElem);
if (add == -1)
{
printf("无更改元素");
return;
}
array[add].data = newElem;
}