数据结构笔记(四)-- 静态链表实现

静态链表

一、静态链表定义

用数组来描述的链表

二、静态链表存储数据类型定义

在这里插入图片描述

typedef int ElemType;//定义存储数据

静态链表结点结构

数据域:用于存储数据元素的值;
游标:其实就是数组下标,表示直接后继元素所在数组中的位置;
线性表的静态单链表存储结构
typedef struct
{
	ElemType data;//数据域
	int cur;//游标(Cursor类似于指针域),相当于单链表中的next指针,存放该元素的后继在数组中的下标,为0时表示无指向
}component, SLinkList[MAX_SIZE];
//component 是变量类型,而SLinkList[MAX_SIZE]是component类型的长度为MAX_SIZE数组。
//SLinkList L 等价于 component L[MAX_SIZE];

静态链表的存储数据思路

1、一般我们对数组第一个和最后一个元素作为特殊元素处理,不存数据。
2、通常把未使用的数组元素称为备用链表。
3、而数组第一个元素,即下标为0的元素的cur就存放备用链表的第一个结点的下标;而数组的最后一个元素的cur则存放第一个有数值的元素的下标,相当于单链表中的头结点作用,当整个链表为空时,则为0在这里插入图片描述

在这里插入图片描述

静态表的实现

1、构造一个空的链表L

在这里插入图片描述

void InitList(SLinkList L)
{ // 构造一个空的链表L,表头为L的最后一个单元L[MAX_SIZE-1],其余单元链成
  // 一个备用链表,表头为L的第一个单元L[0],“0”表示空指针
	int i;
	L[MAX_SIZE - 1].cur = 0; // L的最后一个单元为空链表的表头,初始化表头指向空
	for (i = 0; i < MAX_SIZE - 2; i++) // 将其余单元链接成以L[0]为表头的备用链表
		L[i].cur = i + 1;
	L[MAX_SIZE - 2].cur = 0;
}

2、获取静态链表的长度

从第一个结点元素开始计数直到末尾游标为0的结点

int ListLength(SLinkList L)
{ // 返回L中数据元素个数
	int j = 0, i = L[MAX_SIZE - 1].cur; // i指向第一个元素(i此时为头结点的cur值)
	while (i) // 没到静态链表尾
	{
		i = L[i].cur; // 指向下一个元素
		j++;
	}
	return j;
}

3、判断静态链表是否为空表

如果数据链表的头结点的游标为0,则静态链表为空

bool ListEmpty(SLinkList L)
{ // 若L是空表,返回TRUE;否则返回FALSE
	if (L[MAX_SIZE - 1].cur == 0) // 头结点游标cur为0则是空表
		return true;
	else
		return false;
}

4、向数据链表中追加元素

在这里插入图片描述

bool ListAddElem(SLinkList L,ElemType e)
{
	int i=MAX_SIZE-1,k=0;
	while (i != 0)
	{
		k = i;//保存倒数第二个结点的游标,此游标是最后一个节点的下标
		i = L[i].cur;
	}
	//创建新节点
	int NewCur = 0;
	NewCur = L[0].cur;//获取备用链表的第一个结点元素
	if (NewCur == 0)//如果备用链表为空,追加失败!
	{
		printf("备用链表为空,数据链表已满,追加失败\n");
		return false;
	}
	else //如果备用链表不为空,将游标为NewCur的结点追加数据链表的尾部!
	{
		
		L[k].cur = NewCur;//追加到尾部
		L[0].cur = L[NewCur].cur;//备用链表头结点游标指向下一个结点
		L[NewCur].data = e;//作为数据链表的尾部结点,数据域要赋值
		L[NewCur].cur = 0;//作为数据链表的尾部结点,游标要置0
		return true;
	}
}

5、遍历静态链表

void ListTraverse(SLinkList L)
{
	int i = L[MAX_SIZE - 1].cur;//游标指向首节点
	if (ListEmpty(L))//静态表为空结束程序
	{
		printf("链表为空,遍历失败\n");
		exit(-1);
	}
	else
	{
		while (i != 0)//遍历输出
		{	
			printf("   %d", L[i].data);
			i = L[i].cur;
		}
		printf("\n");
	}
}

6、用e返回数据链表L中第i个结点元素的值

在这里插入图片描述

bool GetElem(SLinkList L,  int i, ElemType &e)
{ // 用e返回L中第i个结点元素的值
	int l, k = MAX_SIZE - 1; // k指向头结点
	if (i<1 || i>ListLength(L)) // i值不合理
		return false;
	for (l = 1; l <= i; l++) // 移动i个元素
		k = L[k].cur;
	e = L[k].data;
	return true;
}

7、在静态单链表L中查找第1个值为e的元素。

int LocateElem(SLinkList L, ElemType e) 
{ // 在静态单链表L中查找第1个值为e的元素。
  // 若找到,则返回它在L中的位序,否则返回0。(与其它LocateElem()的定义不同)
	int i = L[MAX_SIZE-1].cur; // i指示表中第一个结点
	while (i&&L[i].data != e) // 在表中顺链查找(e不能是字符串)
		i = L[i].cur;
	return i;
}

8、找到值为cur_e的前驱结点值

在这里插入图片描述

bool PriorElem(SLinkList L,  ElemType cur_e, ElemType &pre_e)
{ // 初始条件:在L中表头位序为[MAX_SIZE - 1]的静态单链表已存在
  // 操作结果:若cur_e是此单链表的数据元素,且不是第一个,
  //           则用pre_e返回它的前驱,否则操作失败,pre_e无定义
	int j, i = L[MAX_SIZE - 1].cur; // i为链表第一个结点的位置
	if (L[i].data == cur_e)
	{
		printf("链表首结点没有前驱\n");
		return false;
	}
	do
	{ // 向后移动结点
		j = i;
		i = L[i].cur;
	} while (i&&cur_e != L[i].data);//判断后移一个结点元素的值是否等于cur_e
	if (i) //是否移动到尾结点之后(游标值为尾结点的cur值) 如果没有则,找到该元素,并取出其前驱结点元素值,如果是返回false
	{
		pre_e = L[j].data;
		return true;
	}
	return false;
}

9、查找链表中第一个值为cur_e的结点元素的后继结点值

在这里插入图片描述

bool NextElem(SLinkList L,  ElemType cur_e, ElemType &next_e)
{ // 初始条件:在静态单链表L已存在
  // 操作结果:若cur_e是此单链表的数据元素,且不是最后一个,
  // 则用next_e返回它的后继,否则操作失败,next_e无定义
	int i;
	i = LocateElem(L,  cur_e); // 在链表中查找第一个值为cur_e的元素的位置
	if (i) // 在静态单链表中存在元素cur_e
	{
		i = L[i].cur; // cur_e的后继的位置
		if (i) // cur_e有后继
		{
			next_e = L[i].data;
			return true; // cur_e元素有后继
		}
	}
	return false; // L不存在cur_e元素,cur_e元素无后继
}

10、在静态链表中的第i个结点前插入一个值为e的结点

在这里插入图片描述

bool ListInsert(SLinkList L, int i, ElemType e)
{
	int  k = 0,j = MAX_SIZE -1;//游标j指向数据链表头结点
	if (i <= 0 )
	{
		return false;
	}
	while (k < i - 1) // 找到第i-1个结点
	{
		j = L[j].cur;
		k++;
	}
	if (k > i - 1)
	{
		return false;
	}
	//获取新节点
	int NewCur = 0;
	NewCur = L[0].cur;//获取备用链表的第一个结点元素
	if (NewCur == 0)//如果备用链表为空,追加失败!
	{
		printf("备用链表为空,数据链表已满,追加失败\n");
		return false;
	}
	else                            //如果备用链表不为空,将游标为NewCur的结点追加数据链表的尾部!
	{

		L[0].cur = L[NewCur].cur;//备用链表头结点游标指向备用链表的第二个结点
		L[NewCur].data = e;//新节点数据域要赋值
		L[NewCur].cur = L[j].cur;//第i个结点挂载到新结点上
		L[j].cur = NewCur;//新节点挂载到第(i-1)个结点
		return true;
	}

11、删除第i个元素,并返回删除结点的值

在这里插入图片描述
在这里插入图片描述

bool ListDelete(SLinkList L, int i, ElemType &e)
{ // 删除在L中第i个数据元素e,并返回其值
	int j, k = MAX_SIZE - 1; // k指向表头
	if (i<1 || i>ListLength(L))//排除不在链表中的元素
		return false;
	for (j = 1; j < i; j++) // 移动i-1个元素
		k = L[k].cur;//找到要删除节点元素的前一个结点
	j = L[k].cur;//游标j指向第i个节点
	L[k].cur = L[j].cur;//第i-1个节点的游标指向第i+1个节点
	e = L[j].data;//获取到要删除的节点i的值
	Free(L, j);//将要删除的结点给插入到备用链表的第一个结点前
	return true;
}

11.1、向备用链表的首结点前插入游标为k的结点

在这里插入图片描述


void Free(SLinkList L, int k) // 
{ // 将下标为k的空闲结点回收到备用链表(成为备用链表的第一个结点)
	L[k].cur = L[0].cur; // 回收结点的"游标"指向备用链表的第一个结点
	L[0].cur = k; // 备用链表的头结点指向新回收的结点
}

最后给出全部代码

#include<stdio.h> // EOF(=^Z或F6),NULL
#include<stdlib.h> // atoi()
#include<malloc.h> // malloc()等
#define MAX_SIZE 100 // 链表的最大长度

typedef int ElemType;//定义存储数据

// c2-3.h 线性表的静态单链表存储结构
typedef struct
{
	ElemType data;//数据域
	int cur;//游标(Cursor类似于指针域),相当于单链表中的next指针,存放该元素的后继在数组中的下标,为0时表示无指向
}component, SLinkList[MAX_SIZE];
//component 是变量类型,而SLinkList[MAX_SIZE]是component类型的长度为MAX_SIZE数组。
//SLinkList L 等价于 component L[MAX_SIZE];



//1、构造一个空的链表L
void InitList(SLinkList L);
//2、获取静态链表长度
int ListLength(SLinkList L);
//3、判断静态链表是否为空表
bool ListEmpty(SLinkList L);
//4、添加元素
bool ListAddElem(SLinkList L, ElemType e);
//5、遍历静态链表
void ListTraverse(SLinkList L);
//6、用e返回L中第i个结点元素的值
bool GetElem(SLinkList L, int i, ElemType &e);
//7、在静态单链表L中查找第1个值为e的元素。
int LocateElem(SLinkList L, ElemType e);
//8、找到值位cur_e的前驱结点值
bool PriorElem(SLinkList L, ElemType cur_e, ElemType &pre_e);
//9、查找链表中第一个值为cur_e的结点元素的后继结点值
bool NextElem(SLinkList L, ElemType cur_e, ElemType &next_e);
//10、在静态链表中的第i个结点前插入一个值为e的结点
bool ListInsert(SLinkList L, int i, ElemType e);
//11、删除第i个元素,并返回删除结点的值
bool ListDelete(SLinkList L, int i, ElemType &e);
//11.1、向备用链表的首结点前插入游标为k的结点
void Free(SLinkList L, int k);
int main()
{
	SLinkList L;//定义一个长度为100的数据类型为component的数组
	InitList(L);
	printf("静态链表的长度为 %d\n",ListLength(L));
	int length = 10;
	for (int i = 0; i < length; i++)
	{
		ListAddElem(L, 2 * i);
	}
	printf("静态链表的长度为 %d\n", ListLength(L));
	ListTraverse(L);
	int e,e1 = 88,cur_e = 10,pre_e = 0, next_e = 0;
	GetElem(L, 10, e);
	printf("获取到的链表的第10个结点元素值为 %d\n",e);

	printf("查找到的值为 %d 的结点的序位为 %d\n",e,LocateElem( L, e));

	PriorElem(L, cur_e, pre_e);
	printf("结点值为%d 的结点的前驱值为 %d\n", cur_e,pre_e);

	NextElem(L, cur_e, next_e);
	printf("结点值为%d 的结点的后继值为 %d\n", cur_e,next_e);

	ListInsert(L, 9, e1);
	printf("插入的元素为%d\n", e);
	ListTraverse(L);
	ListDelete(L, 9, e);
	printf("删除的元素为%d\n", e);
	ListTraverse(L);


	return 0;
}
//1、构造一个空的链表L
void InitList(SLinkList L)
{ // 构造一个空的链表L,表头为L的最后一个单元L[MAX_SIZE-1],其余单元链成
  // 一个备用链表,表头为L的第一个单元L[0],“0”表示空指针
	int i;
	L[MAX_SIZE - 1].cur = 0; // L的最后一个单元为空链表的表头,初始化表头指向空
	for (i = 0; i < MAX_SIZE - 2; i++) // 将其余单元链接成以L[0]为表头的备用链表
		L[i].cur = i + 1;
	L[MAX_SIZE - 2].cur = 0;
}

//2、获取静态链表长度
int ListLength(SLinkList L)
{ // 返回L中数据元素个数
	int j = 0, i = L[MAX_SIZE - 1].cur; // i指向第一个元素(i此时为头结点的cur值)
	while (i) // 没到静态链表尾
	{
		i = L[i].cur; // 指向下一个元素
		j++;
	}
	return j;
}

//3、判断静态链表是否为空表
bool ListEmpty(SLinkList L)
{ // 若L是空表,返回TRUE;否则返回FALSE
	if (L[MAX_SIZE - 1].cur == 0) // 头结点游标cur为0则是空表
		return true;
	else
		return false;
}

//4、向数据链表中追加元素
bool ListAddElem(SLinkList L,ElemType e)
{
	int i=MAX_SIZE-1,k=0;
	while (i != 0)
	{
		k = i;//保存倒数第二个结点的游标,此游标是最后一个节点的下标
		i = L[i].cur;
	}
	//创建新节点
	int NewCur = 0;
	NewCur = L[0].cur;//获取备用链表的第一个结点元素
	if (NewCur == 0)//如果备用链表为空,追加失败!
	{
		printf("备用链表为空,数据链表已满,追加失败\n");
		return false;
	}
	else                            //如果备用链表不为空,将游标为NewCur的结点追加数据链表的尾部!
	{
		
		L[k].cur = NewCur;//追加到尾部
		L[0].cur = L[NewCur].cur;//备用链表头结点游标指向下一个结点
		L[NewCur].data = e;//作为数据链表的尾部结点,数据域要赋值
		L[NewCur].cur = 0;//作为数据链表的尾部结点,游标要置0
		return true;
	}
}

//5、遍历静态链表
void ListTraverse(SLinkList L)
{
	int i = L[MAX_SIZE - 1].cur;//游标指向首节点
	if (ListEmpty(L))//静态表为空结束程序
	{
		printf("链表为空,遍历失败\n");
		exit(-1);
	}
	else
	{
		while (i != 0)//遍历输出
		{	
			printf("   %d", L[i].data);
			i = L[i].cur;
		}
		printf("\n");
	}
}

//6、用e返回数据链表L中第i个结点元素的值
bool GetElem(SLinkList L,  int i, ElemType &e)
{ // 用e返回L中第i个结点元素的值
	int l, k = MAX_SIZE - 1; // k指向头结点
	if (i<1 || i>ListLength(L)) // i值不合理
		return false;
	for (l = 1; l <= i; l++) // 移动i个元素
		k = L[k].cur;
	e = L[k].data;
	return true;
}

//7、在静态单链表L中查找第1个值为e的元素。
int LocateElem(SLinkList L, ElemType e) 
{ // 在静态单链表L中查找第1个值为e的元素。
  // 若找到,则返回它在L中的位序,否则返回0。(与其它LocateElem()的定义不同)
	int i = L[MAX_SIZE-1].cur; // i指示表中第一个结点
	while (i&&L[i].data != e) // 在表中顺链查找(e不能是字符串)
		i = L[i].cur;
	return i;
}

//8、找到值位cur_e的前驱结点值
bool PriorElem(SLinkList L,  ElemType cur_e, ElemType &pre_e)
{ // 初始条件:在L中表头位序为[MAX_SIZE - 1]的静态单链表已存在
  // 操作结果:若cur_e是此单链表的数据元素,且不是第一个,
  //           则用pre_e返回它的前驱,否则操作失败,pre_e无定义
	int j, i = L[MAX_SIZE - 1].cur; // i为链表第一个结点的位置
	if (L[i].data == cur_e)
	{
		printf("链表首结点没有前驱\n");
		return false;
	}
	do
	{ // 向后移动结点
		j = i;
		i = L[i].cur;
	} while (i&&cur_e != L[i].data);//判断后移一个结点元素的值是否等于cur_e
	if (i) //是否移动到尾结点之后(游标值为尾结点的cur值) 如果没有则,找到该元素,并取出其前驱结点元素值,如果是返回false
	{
		pre_e = L[j].data;
		return true;
	}
	return false;
}

//9、查找链表中第一个值为cur_e的结点元素的后继结点值
bool NextElem(SLinkList L,  ElemType cur_e, ElemType &next_e)
{ // 初始条件:在静态单链表L已存在
  // 操作结果:若cur_e是此单链表的数据元素,且不是最后一个,
  // 则用next_e返回它的后继,否则操作失败,next_e无定义
	int i;
	i = LocateElem(L,  cur_e); // 在链表中查找第一个值为cur_e的元素的位置
	if (i) // 在静态单链表中存在元素cur_e
	{
		i = L[i].cur; // cur_e的后继的位置
		if (i) // cur_e有后继
		{
			next_e = L[i].data;
			return true; // cur_e元素有后继
		}
	}
	return false; // L不存在cur_e元素,cur_e元素无后继
}

//10、在静态链表中的第i个结点前插入一个值为e的结点
bool ListInsert(SLinkList L, int i, ElemType e)
{
	int  k = 0,j = MAX_SIZE -1;//游标j指向数据链表头结点
	if (i <= 0 )
	{
		return false;
	}
	while (k < i - 1) // 找到第i-1个结点
	{
		j = L[j].cur;
		k++;
	}
	if (k > i - 1)
	{
		return false;
	}
	//获取新节点
	int NewCur = 0;
	NewCur = L[0].cur;//获取备用链表的第一个结点元素
	if (NewCur == 0)//如果备用链表为空,追加失败!
	{
		printf("备用链表为空,数据链表已满,追加失败\n");
		return false;
	}
	else                            //如果备用链表不为空,将游标为NewCur的结点追加数据链表的尾部!
	{

		L[0].cur = L[NewCur].cur;//备用链表头结点游标指向备用链表的第二个结点
		L[NewCur].data = e;//新节点数据域要赋值
		L[NewCur].cur = L[j].cur;//第i个结点挂载到新结点上
		L[j].cur = NewCur;//新节点挂载到第(i-1)个结点
		return true;
	}
}
//10.1、从备用链表中取出其首结点,
int Malloc(SLinkList L) 
{ // 若备用链表非空,则返回分配的结点下标(备用链表的第一个结点),否则返回0
	int NewCur = L[0].cur;
	if (NewCur) // 备用链表非空
		L[0].cur = L[NewCur].cur; // 备用链表的头结点指向原备用链表的第二个结点
	return NewCur; // 返回新开辟结点的坐标
}

//11.1、向备用链表的首结点前插入游标为k的结点
void Free(SLinkList L, int k) 
{ // 将下标为k的空闲结点回收到备用链表(成为备用链表的第一个结点)
	L[k].cur = L[0].cur; // 回收结点的"游标"指向备用链表的第一个结点
	L[0].cur = k; // 备用链表的头结点指向新回收的结点
}
//11、删除第i个元素,并返回删除结点的值
bool ListDelete(SLinkList L, int i, ElemType &e)
{ // 删除在L中第i个数据元素e,并返回其值
	int j, k = MAX_SIZE - 1; // k指向表头
	if (i<1 || i>ListLength(L))//排除不在链表中的元素
		return false;
	for (j = 1; j < i; j++) // 移动i-1个元素
		k = L[k].cur;//找到要删除节点元素的前一个结点
	j = L[k].cur;//游标j指向第i个节点
	L[k].cur = L[j].cur;//第i-1个节点的游标指向第i+1个节点
	e = L[j].data;//获取到要删除的节点i的值
	Free(L, j);//将要删除的结点给插入到备用链表的第一个结点前
	return true;
}

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值