文章目录
- 1 前言
- 2 什么是静态链表
- 2.1 数据表与备用表
- 2.2 静态链表优点
- 2.3 静态链表不足
- 2.4 静态链表应用场景
- 3 静态链表创建
- 4 静态链表清空与销毁
- 5 静态链表查找
- 6 静态链表插入
- 7 静态链表删除
- 8 实例
1 前言
在前面文章描述的单链表、双向链表、循环链表,都属于“动态链表”。与动态链表对应的就是静态链表,静态链表综合了顺序表和链表的优点。本文描述静态链表的含义、实现以及操作。
2 什么是静态链表
用静态结构体数组实现的链表称为静态链表,其中元素节点是一个结构体变量,包含了数据域(data)和游标(cur)。
/* 静态链表节点数据结构 */
struct _static_link_list_node
{
int data;
int cur;
}
- 数据域,存储的是有效数据
- 游标,与动态链表的指针类似,用于描述数据之间的逻辑关系;当前节点的游标存储的是下一有效数据的地址(数组索引号)
2.1 数据表与备用表
静态链表需要维护两条链路,一条是维护已使用的节点链路(有效数据链路),称为数据表。一条是维护未使用的节点链路(数据元素为空的备用链路),称为备用表。对于备用表,元素节点的游标存储的是下一空节点的地址。
与普通链表一样,静态链表同样存在带头结点和不带头结点的情况,为了简化链表维护过程,一般也是使用带头结点的静态链表。由于静态链表需维护两个表,通常我们会使用头尾元素节点作为两个表的头结点。元素第首节点作为备用表的头结点,元素末尾节点作为有效数据表的节点。这样的好处是,空表从首节点开始变量,有效数据表从末尾节点开始遍历;插入数据时只需访问首节点即可快速获取空闲链表地址。
静态链表空表特点:
- 备用链表节点游标值存储下一空闲节点的数组下标,如上图灰色区域就是空闲节点
- 备用表最后节点游标值为0,如上图中的
“a6”
节点 - 数据表头指针游标值为0
静态链表非空表特点:
- 数据表头指针游标为值为第一个有效节点
- 最后一个有效数据节点游标值为0,如上图中的“a2”节点
注
备用链表表头通常是使首节点;有效数据链表表头可以使用任意节点,为了方便管理,一般使用末尾节点或者第二个节点。
2.2 静态链表优点
静态链表综合了顺序表和链表(链表一般默认指的是动态链表)的特点,因此除了包含了两者的特点外,也兼并了两者的优点,即是:
- 高效的查找操作(顺序表)
- 高效的删除、插入操作(链表)
此外,静态链表由游标建立节点关系,因此静态链表元素节点可以任意存放,也可以顺序存放。
2.3 静态链表不足
链表兼顾了顺序表和链表的优点,但也继承了顺序表的不足,以及引入自身的不足。
-
需要提前申请内存,不能动态增加链表容量
-
维护两条链表,一条保存已使用的节点,一条保存未使用的节点
-
失去顺序存储结构随机存取的特性
2.4 静态链表应用场景
静态链表综合了顺序表和链表的优点,适用于“既要高效访问元素,又能快速增删数据元素节点”的场景。此外,静态链表还用于一些不支持指针的编程语言中。
3 静态链表创建
静态链表创建,一般是创建一个空的链表,这里我们创建一个带头节点的空静态链表。这里通过malloc
申请一块连续内存作为静态链表存储空间,注意事项包括:
- 多申请两个节点作为备用表和数据表头节点,不存储有效数据
- 备用表节点游标赋值
- 备用表最后节点游标值为0
- 数据表头节点游标值为0
静态链表创建外代码:
static_link_list_t *create_static_link_list(int capacity)
{
static_link_list_t *list = NULL;
int i = 0;
if (capacity >= 0)
{
list = (static_link_list_t*)malloc(sizeof(static_link_list_t) +
sizeof(list_node_type_t) * (capacity+2));/* 多申请2节点作头节点 */
}
if (list != NULL)
{
list->capacity = capacity;
list->node = (list_node_type_t*)(list+1);
for (i=0; i<capacity+2; i++)
{
list->node[i].data = 0; /* 备用表节点游标赋值 */
list->node[i].cur = i+1;
}
list->node[capacity].cur = 0; /* 备用表最后一个节点游标值为0 */
list->node[capacity+1].cur = 0; /* 数据表头节点游标值为0 */
}
return list;
}
4 静态链表清空与销毁
静态链表清空指的是删除链表有效元素节点,释放节点内存空间。静态链表销毁指的是删除所有节点,包括头结点,并释放节点内存空间。双向链表清空与销毁都是遍历整个链表。
静态链表清空伪代码:
int clear_static_link_list(static_link_list_t *list)
{
int i = 0;
if (list==NULL)
{
return 0;
}
for (i=0; i<list->capacity+2; i++)
{
list->node[i].data = 0;
list->node[i].cur = i+1;
}
list->node[list->capacity].cur = 0;
list->node[list->capacity+1].cur = 0;
return 0;
}
静态链表删除伪代码:
int destory_static_link_list(static_link_list_t *list)
{
static_link_list_t *p = NULL;
if (list==NULL)
{
return 0;
}
free(list);
list = NULL;
return 0;
}
5 静态链表查找
静态链表与普通链表的查找方式一样,有两种方式,分别是根据元素节点索引号(数组下标)和节点数据元素值查找,这两种方式都需要从链表头开始遍历链表,直至查找到指定节点。对于元素值查找,只适用于链表中存储的元素值都是唯一的情况,否则只能使用节点索引号查找。
如下图,假设需查找“a3”
节点,遍历过程如下:
【1】首先根据头节点游标值1找到a1
【2】根据节点a1游标值2找到a2
【3】根据节点a2游标值3找到a3
静态链表查找伪代码:
int get_static_link_list_node(static_link_list_t *list, int pos, list_node_type_t *rnode)
{
int index,i;
list_node_type_t *p = NULL;
index = (list->capacity+2) - 1;
for (i=1; i<pos; i++)
{
index = list->node[index].cur;
}
i = list->node[index].cur;
rnode->data = list->node[i].data;
rnode->cur = list->node[i].cur;
return 0;
}
6 静态链表插入
静态链表插入时间复杂度为O(1),插入操作首先需遍历查找到目标位置的前一节点,大体步骤如下:
【1】申请存储节点,从备用表获取一个空闲节点空间并赋数据值
【2】遍历查找到插入位置前一节点
【3】插入节点,插入位置前一节点游标赋值给待插入节点游标
【4】更改游标关系,待插入节点数组下标赋值给插入位置前一节点游标
如下图,左则为原始静态链表;在“a2”的位置插入一个数据值为d的有效节点,插入后的静态表如图右则,数据表样式为:d0—>d—>d1。
静态链表插入伪代码:
int insert_static_link_list_node(static_link_list_t *list, int value, int pos)
{
int findex,iindex,i,j,k;
if(pos<1 || (pos>list->capacity))
{
return -1;
}
findex = get_static_link_list_free_index(list);
if (findex <= 0)
{
return -1; /* 链表已满 */
}
iindex = (list->capacity+2) - 1;
for (i=1; i<pos; i++)
{
iindex = list->node[iindex].cur;
}
list->node[findex].data = value;
list->node[findex].cur = list->node[iindex].cur;
list->node[iindex].cur = findex;
return 0;
}
通过与前面文章单链表比较,静态链表的插入操作与单链表一样,也是需查找到插入目标位置的前一节点。这也是单向链表的特性,双向链表则可以直接操作插入目标位置。
7 静态链表删除
静态链表删除时间复杂度为O(1)。静态链表删除与插入是一个相反的的过程,删除操作首先需遍历查找到目标位置的前一节点,大体步骤如下:
【1】遍历查找到插入位置前一节点
【3】删除节点,插入位置节点游标值赋给前一节点游标
【4】标记为空闲节点,备用表头结点游标值赋给插入位置节点游标
【5】更改备用头结点,插入位置节点索引号(数组下标)值赋给备用表头结点游标
如下图,左则为原始静态链表;删除“a2”节点数据,删除后的静态表如图右则,数据表样式为:d0—>d2。
静态链表删除伪代码:
bool delete_static_link_list_node(static_link_list_t *list, int pos)
{
int i,j,index;
list_node_type_t node;
index = (list->capacity+2) - 1;
for (i=1; i<pos; i++)
{
index = list->node[index].cur;
}
/* 获取目标位置 */
j = list->node[index].cur;
list->node[index].cur = list->node[j].cur;
/* 设置备用链表 */
list->node[j].cur = list->node[0].cur;
list->node[0].cur = j;
return 0;
}
8 实例
- 实现一个带头节点的静态链表,头节点不纳入链表长度计算
- 提供静态链表创建、长度查询、空表检查、插入、删除、查找、清空、销毁操作接口
- 提供节点索引号(数组下标)查找实现方法
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <stdbool.h>
/* 静态链表节点 */
typedef struct _static_link_list_node
{
int data;
int cur;
}list_node_type_t;
typedef struct _static_link_list
{
int capacity;
list_node_type_t *node;
}static_link_list_t;
#define SLINK_LIST_SIZE (10)
static_link_list_t *create_static_link_list(int capacity)
{
static_link_list_t *list = NULL;
int i = 0;
if (capacity >= 0)
{
list = (static_link_list_t*)malloc(sizeof(static_link_list_t) +
sizeof(list_node_type_t) * (capacity+2));/* 多申请2节点作头节点 */
}
if (list != NULL)
{
list->capacity = capacity;
list->node = (list_node_type_t*)(list+1);
for (i=0; i<capacity+2; i++)
{
list->node[i].data = 0; /* 备用表节点游标赋值 */
list->node[i].cur = i+1;
}
list->node[capacity].cur = 0; /* 备用表最后一个节点游标值为0 */
list->node[capacity+1].cur = 0; /* 数据表头节点游标值为0 */
}
return list;
}
/* 获取静态链表有效节点数,不包括头节点 */
int get_static_link_list_occupy(static_link_list_t *list)
{
#if 1
int i = 0;
int j = 0;
i = list->node[list->capacity+2-1].cur;
while (i > 0)
{
j++;
i = list->node[i].cur;
}
return j;
#else
if (list == NULL)
{
return 0;
}
return list->occupy;
#endif
}
int clear_static_link_list(static_link_list_t *list)
{
int i = 0;
if (list==NULL)
{
return 0;
}
if (get_static_link_list_occupy(list) == 0)
{
return 0;
}
for (i=0; i<list->capacity+2; i++)
{
list->node[i].data = 0;
list->node[i].cur = i+1;
}
list->node[list->capacity].cur = 0;
list->node[list->capacity+1].cur = 0;
return 0;
}
int destory_static_link_list(static_link_list_t *list)
{
static_link_list_t *p = NULL;
if (list==NULL)
{
return 0;
}
free(list);
list = NULL;
return 0;
}
int get_static_link_list_node(static_link_list_t *list, int pos, list_node_type_t *rnode)
{
int index,i;
list_node_type_t *p = NULL;
if ((list == NULL)||(pos<1))
{
return -1;
}
if (get_static_link_list_occupy(list) == 0)
{
return -1;
}
index = (list->capacity+2) - 1;
for (i=1; i<pos; i++)
{
index = list->node[index].cur;
}
i = list->node[index].cur;
rnode->data = list->node[i].data;
rnode->cur = list->node[i].cur;
return 0;
}
/* 获取备用链表可用索引号 */
int get_static_link_list_free_index(static_link_list_t *list)
{
int i;
if (list==NULL)
{
return -1;
}
i = list->node[0].cur;
if (i>0)
{
list->node[0].cur = list->node[i].cur;
}
return i;
}
/* 静态链表pos位置插入value */
int insert_static_link_list_node(static_link_list_t *list, int value, int pos)
{
int findex,iindex,i,j,k;
if (list==NULL)
{
return -1;
}
if(pos<1 || (pos>list->capacity))
{
return -1;
}
findex = get_static_link_list_free_index(list);
if (findex <= 0)
{
return -1; /* 链表已满 */
}
iindex = (list->capacity+2) - 1;
for (i=1; i<pos; i++)
{
iindex = list->node[iindex].cur;
}
list->node[findex].data = value;
list->node[findex].cur = list->node[iindex].cur;
list->node[iindex].cur = findex;
return 0;
}
/* 删除一个静态链表节点 */
int delete_static_link_list_node(static_link_list_t *list, int pos)
{
int i,j,index;
list_node_type_t node;
if (list==NULL)
{
return -1;
}
if (pos<1 || (pos>=get_static_link_list_occupy(list)))
{/* 删除位置超出范围 */
return -1;
}
index = (list->capacity+2) - 1;
for (i=1; i<pos; i++)
{
index = list->node[index].cur;
}
/* 获取目标位置 */
j = list->node[index].cur;
list->node[index].cur = list->node[j].cur;
/* 设置备用链表 */
list->node[j].cur = list->node[0].cur;
list->node[0].cur = j;
return 0;
}
/* 输出静态链表所有节点信息 */
void printf_static_link_list_all_node(static_link_list_t *list)
{
int index = 0;
for (index=0; index<(list->capacity+2); index++)
{
printf("node%d[%d, %d] ", index, list->node[index].data, list->node[index].cur);
}
printf("n");
}
/* 输出静态链表有效数据 */
void printf_static_link_list_data_node(static_link_list_t *list)
{
int index = 0;
index = (list->capacity+2) - 1;
printf("[head,%d] ", list->node[index].cur);
while (list->node[index].cur > 0)
{
index = list->node[index].cur;
printf("[%d, %d] ", list->node[index].data, list->node[index].cur);
}
printf("n");
}
int main(int argc, char *argv[])
{
static_link_list_t *linklist = NULL;
static_link_list_t *ptemp;
list_node_type_t node;
int elem = 0;
int i;
/* 创建静态链表 */
linklist = create_static_link_list(SLINK_LIST_SIZE);
/* 插入操作 */
insert_static_link_list_node(linklist, 1, 1);
insert_static_link_list_node(linklist, 2, 2);
insert_static_link_list_node(linklist, 3, 1);
insert_static_link_list_node(linklist, 5, 1);
printf("static link list node:n");
printf_static_link_list_all_node(linklist);
printf("static link list occupy:[%d]n", get_static_link_list_occupy(linklist));
printf("static link list data node:n");
printf_static_link_list_data_node(linklist);
/* 查找操作 */
get_static_link_list_node(linklist, 2, &node);
printf("fine the data node[2], value:[%d, %d]n", node.data, node.cur);
/* 删除操作 */
printf("delete data node[2]n");
delete_static_link_list_node(linklist, 2);
printf("static link list node:n");
printf_static_link_list_all_node(linklist);
printf("static link list occupy:[%d]n", get_static_link_list_occupy(linklist));
printf("static link list data node:n");
printf_static_link_list_data_node(linklist);
destory_static_link_list(linklist); /* 销毁静态链表 */
}
编译执行
- 在Ubuntu16.04下执行结果
acuity@ubuntu:/mnt/hgfs/LSW/STHB/temp$ gcc static_link_list.c -o static_link_list
acuity@ubuntu:/mnt/hgfs/LSW/STHB/temp$ ./static_link_list
static link list node:
node0[0, 5] node1[1, 2] node2[2, 0] node3[3, 1] node4[5, 3] node5[0, 6] node6[0, 7] node7[0, 8] node8[0, 9] node9[0, 10] node10[0, 0] node11[0, 4]
static link list occupy:[4]
static link list data node:
[head,4] [5, 3] [3, 1] [1, 2] [2, 0]
fine the data node[2], value:[3, 1]
delete data node[2]
static link list node:
node0[0, 3] node1[1, 2] node2[2, 0] node3[3, 5] node4[5, 1] node5[0, 6] node6[0, 7] node7[0, 8] node8[0, 9] node9[0, 10] node10[0, 0] node11[0, 4]
static link list occupy:[3]
static link list data node:
最后
以上就是闪闪彩虹最近收集整理的关于数据结构回顾——静态链表操作详解及C语言实现1 前言2 什么是静态链表3 静态链表创建4 静态链表清空与销毁5 静态链表查找6 静态链表插入7 静态链表删除8 实例的全部内容,更多相关数据结构回顾——静态链表操作详解及C语言实现1内容请搜索靠谱客的其他文章。
发表评论 取消回复