一 概述
1 2 3 4
| 1.什么是静态链表 2.静态链表中的节点 3.备用链表 4.静态链表的创建
|
二 什么是静态链表
2.1 概念
1 2 3 4
| 静态链表也是线性存储结构的一种,它兼顾顺序表和链表的优点,是顺序表和链表的升级版。
使用静态链表存储数据,数据全部存储在数组中(和顺序表一样), 但存储位置是随机的,数据之间“一对一”的逻辑关系通过一个整形变量(“游标”,和指针功能类似)维持(和链表类似)
|
2.2 静态链表的存储过程
使用静态链表存储{1,2,3}
的过程如下
一、创建一个足够大的数组,假设大小为 6

二、在将数据存放到数组中时,给各个数据元素配备一个整形变量,用于指明各个元素的直接后继元素所在数组中的位置下标

2.3 存储说明
1 2 3 4 5 6 7
| 通常,静态链表会将第一个数据元素放到数组下标为 1 的位置(a[1])中。
上图中,从a[1]存储的数据元素 1 开始,通过存储的游标变量 3,就可以在 a[3] 中找到元素 1 的直接后继元素 2; 同样,通过元素 a[3] 存储的游标变量 5,可以在 a[5] 中找到元素 2 的直接后继元素 3, 这样的循环过程直到某元素的游标变量为 0 截止(因为a[0]默认不存储数据元素)。 通过 "数组+游标" 的方式存储具有线性关系数据的存储结构就是静态链表。
|
三 静态链表中的节点
3.1 静态链表存储数据元素
1 2 3
| 静态链表存储数据元素也需要自定义数据类型,至少需要包含: -数据域:用于存储数据元素的值 -游标:其实就是数组下标,表示直接后继元素所在数组中的位置
|
3.2 静态链表中节点的构成(C 语言)
1 2 3 4
| typedef struct { int data; // 数据域 int cur; // 游标 }component;
|
四 备用链表
4.1 概念
1 2 3 4 5 6 7 8 9 10 11
| 静态链表中,除了数据本身通过游标组成的链表外,还需要有一条连接各个空闲位置的链表,称为备用链表。
备用链表的作用是回收数组中未使用或之前使用过(目前未使用)的存储空间,留待后期使用。
静态链表使用数组申请的物理空间中,存有两个链表:一条连接数据,另一条连接数组中未使用的空间。
通常,备用链表的表头位于数组下标为 0(a[0]) 的位置,而数据链表的表头位于数组下标为 1(a[1])的位置。
静态链表中设置备用链表的好处: -可清楚知道数组中是否有空闲位置,以便数据链表添加新数据时使用。 -比如,若静态链表中数组下标为 0 的位置上存有数据,则证明数组已满。
|
4.2 图示
1 2 3 4
| 例如,使用静态链表存储 `{1,2,3}`,假设使用长度为 6 的数组 a,则存储状态可能如下图:
上图中,备用链表上连接的依次是 `a[0]`、`a[2]` 和 `a[4]`, 而数据链表上连接的依次是 `a[1]`、`a[3]` 和 `a[5]`
|

五 静态链表的创建
5.1 图示
使用静态链表(数组长度为 6)存储 {1,2,3}
需经历以下几个阶段
一、在数据链表未初始化之前,数组中所有位置都处于空闲状态,因此都应被链接在备用链表上
1 2 3 4 5 6
| 当向静态链表中添加数据时,需提前从备用链表中摘除节点,以供新数据使用。
备用链表摘除节点最简单的方法是摘除 a[0] 的直接后继节点; 同样,向备用链表中添加空闲节点也是添加作为 a[0] 新的直接后继节点。 因为a[0]是备用链表的第一个节点, 我们知道它的位置,操作它的直接后继节点相对容易,无需遍历备用链表,耗费的时间复杂度为 O(1)。
|

二、在上图的基础上,向静态链表中添加元素 1 的过程如下图所示

三、在上图的基础上,添加元素 2 的过程如下图所示

四、在上图的基础上,继续添加元素 3 ,过程如下图所示

5.2 创建静态链表的 C 语言实现代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
| #include <stdio.h> #define maxSize 6 typedef struct { int data; int cur; }component; // 将结构体数组中所有分量链接到备用链表中 void reserveArr(component *array); // 初始化静态链表 int initArr(component *array); // 输出函数 void displayArr(component * array,int body); // 从备用链表上摘下空闲节点的函数 int mallocArr(component * array); int main() { component array[maxSize]; int body=initArr(array); printf("静态链表为:\n"); displayArr(array, body); return 0; } // 创建备用链表 void reserveArr(component *array){ for (int i=0; i<maxSize; i++) { // 将每个数组分量链接到一起 array[i].cur=i+1; array[i].data=-1; } // 链表最后一个结点的游标值为 0 array[maxSize-1].cur=0; } // 提取分配空间 int mallocArr(component * array){ // 若备用链表非空, 则返回分配的结点下标, 否则返回 0 (当分配最后一个结点时, 该结点的 // 游标值为 0) int i=array[0].cur; if (array[0].cur) { array[0].cur=array[i].cur; } return i; } // 初始化静态链表 int initArr(component *array){ reserveArr(array); int body=mallocArr(array); // 声明一个变量, 把它当指针使, 指向链表的最后的一个结点, 因为链表为空, 所以和头结点重合 int tempBody=body; for (int i=1; i<4; i++) { // 从备用链表中拿出空闲的分量 int j=mallocArr(array); // 将申请的空闲分量链接在链表的最后一个结点后面 array[tempBody].cur=j; // 给新申请的分量的数据域初始化 array[j].data=i; // 将指向链表最后一个结点的指针后移 tempBody=j; } // 新的链表最后一个结点的指针设置为 0 array[tempBody].cur=0; return body; } void displayArr(component * array,int body){ // tempBody 准备做遍历使用 int tempBody=body; while (array[tempBody].cur) { printf("%d,%d ",array[tempBody].data,array[tempBody].cur); tempBody=array[tempBody].cur; } printf("%d,%d\n",array[tempBody].data,array[tempBody].cur); } /* 静态链表为: -1,2 1,3 2,4 3,0 */
|
说明:
- 此代码创建了一个带有头节点的静态链表,
- 因此最先输出的 "-1,2" 表示的是头节点(-1 表示此处未存储数据),
- 其首元节点(存储元素 1 的节点)在数组
array[2]
中
六 参考