数据结构回顾——静态链表操作详解及C语言实现


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:
  • 10
    点赞
  • 54
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Acuity.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值