静态链表
静态链表:线性存储结构的一种,兼顾顺序表和链表的优点,是顺序表和链表的升级;静态链表的数据全部存储在数组中(顺序表),但存储的位置是随机的,数据直接的一对一关系是通过一个整型变量(称为“游标”,类似指针的功能)维持。
1. 静态链表中的节点
数据域:用于存储数据元素的值
游标:即数组下标,表示直接后继元素所在数组中的位置
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]默认不存储数据元素)
2. 备用链表
静态链表中,除了数据本身通过游标组成链表外,还需要有一条连接各个空闲位置的链表,称为备用链表。
作用:回收数组中未使用或者之前使用过(现在不用)的存储空间,留待后期使用。即静态链表使用数组申请的物理空间中,存在两个链表,一条连接数据,另一条连接数组中为使用的空间。
注:通常备用链表的表头位于数组下标为0(a[0])的位置,而数据链表的表头位于数组下标为1(a[1])的位置。
静态链表中设置备用链表的好处是,可以清楚地知道数组中是否有空闲位置,以便数据链表添加新数据时使用。比如,若静态链表中数组下标为 0 的位置上存有数据,则证明数组已满。
3 静态链表的实现
在数据链表未初始化之前,数组中所有位置都处于空闲状态,所以都链接在备用链表上。(图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)
。
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 #define maxSize 6 5 6 typedef struct 7 { 8 int data; 9 int cur; //游标 10 }component; 11 12 /************************************************************************* 13 创建备用链表 14 *****************************************************************************/ 15 void reserveArr(component *array) 16 { 17 int i; 18 for(i=0;i<maxSize;i++) 19 { 20 array[i].cur=i+1; //将每个数组分量链接到一起 21 } 22 array[maxSize-1].cur=0; //链表最后一个节点的游标为0 23 } 24 25 /******************************************************** 26 提取分配空间 27 若备用链表为非空,则返回分配的节点下标,否则返回0(当分配最后一个节点时,该节点的游标值为0) 28 ********************************************************/ 29 int mallocArr(component *array) 30 { 31 int i=array[0].cur; 32 33 if(array[0].cur) 34 { 35 //array[0].data=0; 36 array[0].cur=array[i].cur; 37 } 38 return i; 39 } 40 41 /*************************************************************************** 42 初始化静态链表 43 ****************************************************************************/ 44 int initArr(component *array) 45 { 46 int body,tempBody,i,j; 47 reserveArr(array); 48 body=mallocArr(array); 49 tempBody=body; //声明一个变量把它当指针使,指向链表的最后一个节点,因为链表为空,所以和头结点重合 50 for(i=1;i<4;i++) 51 { 52 j=mallocArr(array); //从备用链表中拿出空闲的分量 53 array[tempBody].cur=j;//将申请的空闲分量链接在链表的最后一个结点后面 54 array[j].data=i; //给新申请的分量数据域初始化 55 tempBody=j; //将指向链表最后一个结点的指针后移 56 } 57 array[tempBody].cur=0; //新的链表最后一个结点的指针设为0 58 return body; 59 } 60 61 void displayArr(component *array,int body) 62 { 63 int tempBody=body; //tempBody准备遍历使用 64 while(array[tempBody].cur) 65 { 66 printf("%d,%d\n",array[tempBody].data,array[tempBody].cur); 67 tempBody=array[tempBody].cur; 68 } 69 printf("%d,%d\n",array[tempBody].data,array[tempBody].cur); 70 } 71 72 int main() 73 { 74 int body; 75 component array[maxSize]; 76 body=initArr(array); 77 printf("static link:\n"); 78 displayArr(array,body); 79 80 system("pause"); 81 return 0; 82 }
4 静态链表中添加一个元素
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; }
5 静态链表删除元素
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=array[tempBody].cur; } //将被删除结点的游标直接给被删除结点的上一个结点 array[tempBody].cur=array[del].cur; //回收被摘除节点的空间 freeArr(array, del); }
6 静态链表中查找元素
/******************************************************** 在以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,表示在链表中没有找到该元素 }
7 静态链表中更改数据
/******************************************************** 在以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; }