静态单链表的实现

静态单链表存储类型

某些高级语言如 BASIC,没有“指针”数据类型,但有“结构体”数据类型。在这类
高级语言中,若要使用链表结构,就需先开辟一个充分大的结构体数组。结构体的一个成员存放数据,另一个成员(“游标”)存放下一个数的位置(下标),这称为静态链表。输出链表不是按数组的下标顺序输出的,而是由一个指定的位置开始根据游标依次输出的。
在这里插入图片描述
component为结构体,含有数据成员和下一个元素的下标。LinkList为数组名称。

#define MAX_SIZE 100	// 链表的最大长度
typedef int ElemType;
typedef struct {
	ElemType data;
	int cur;	//“游标”,下一个数的下标
}component,LinkList[MAX_SIZE];

静态数组有 2 个链表,一个链表上链接的是线性表的结点,另一个链表(备用链表)上链接的是所有没被使用的结点。
将所有空闲结点链接形成一个备用链表,数组下标为 0 的单元为备用链表的头结点。当线性表需要新结点时,把备用链表中的首元结点(由[0].cur 指示)从备用链表中删除,作为新结点,插入线性表。
当删除线性表中的结点时,被删除的结点插入备用链表中,成为备用链表的首元结点。
从备用链表删除结点或向备用链表插入结点都在表头进行,因为这样效率最高。
生成静态链表的方法可有两种:一种是在一个数组中只生成一个静态链表,这种情况可以固定静态链表的头指针位置,如最后一个单元([MAX_SIZE-1]);另一种是在一个数组中可根据需要生成若干个独立的链表,每个链表的头指针在生成链表时才指定。

注意:
所有的操作函数都不用&L,直接用L,因为L为数组的基地址,所有函数都没有改变。

开辟结点

若备用链表非空,则返回分配的结点下标(备用链表的第一个结点);否则返回0
在这里插入图片描述

int Malloc(LinkList space) {
	int i = space[0].cur;
	if (i)	// 备用链表非空
		space[0].cur = space[i].cur;// 备用链表的头结点指向原备用链表的第二个结点
	return i;	// 返回新开辟结点的坐标
}

释放结点

将下标为k的空闲结点回收到备用链表(成为备用链表的第一个结点)。
在这里插入图片描述

void Free(LinkList space,int k) {
	space[k].cur=space[0].cur;  // 回收结点的"游标"指向备用链表的第一个结点
	space[0].cur = k; // 备用链表的头结点指向新回收的结点
}

基本操作

1.初始化

构造一个空的链表L,表头为L的最后一个单元L[MAX_SIZE-1],其余单元链成一个备用链表,表头为L的第一个单元L[0],“0”表示空指针。
在这里插入图片描述

void InitList(LinkList L) {
	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.销毁、清空

将L重置为空表;i为链表的首结点,k为链表的首结点,j为链表的尾结点。就是将里链表的首结点连接到备用结点的头结点后,再将原来备用链表的首结点连接到链表的尾结点后;将链表的头结点置空。
在这里插入图片描述

void ClearList(LinkList L) {
	int i, j, k;
	i = L[MAX_SIZE - 1].cur;	// 链表首结点的位置
	L[MAX_SIZE - 1].cur = 0;	 // 链表空
	k = L[0].cur;	 // 备用链表首结点的位置
	L[0].cur = i;	// 把链表的首结点连到备用链表的头结点后
	while (i) {	 	// 遍历到链表尾,j用来保存链表尾结点的位置
		j = i;		
		i = L[i].cur;
	}
	L[j].cur = k;		 // 备用链表的首结点接到链表的尾结点后
}

3.为空

bool ListEmpty(LinkList L) {
	if (L[MAX_SIZE - 1].cur == 0)	 // 空表
		return true;
	else
		return false;
}

4.长度

int ListLength(LinkList L) {
	int j = 0, i = L[MAX_SIZE - 1].cur;
	while (i) {
		i = L[i].cur;
		++j;
	}
	return j;
}

5.获取元素
返回L中第i个元素的值

bool GetElem(LinkList L, int i, ElemType& e)
{ 
	int k = MAX_SIZE - 1;	 // k指向表头序号
	if (i<1 || i>ListLength(L))	//i值不合法
		return false;
	for (int j = 1; j <= i; j++)	 // 移动到第i个元素处
		k = L[k].cur;
	e = L[k].data;
	return true;
}

6.定位元素
在静态单链线性表L中查找第1个值为e的元素。若找到,则返回它在L中的位序;否则返回0

int LocateElem(LinkList L, ElemType e)
{
	int i = L[MAX_SIZE - 1].cur;	 // i指示表中第一个结点
	while (i && L[i].data != e)		// 在表中顺链查找
		i = L[i].cur;
	return i;
}

7.前驱
若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱; 否则操作失败,pre_e无定义

bool PriorElem(LinkList L, ElemType cur_e, ElemType& pre_e)
{ 
	int j, i = L[MAX_SIZE - 1].cur; // i指示链表第一个结点的位置
	do{ // 向后移动结点
		j = i;
		i = L[i].cur;
	} while (i && cur_e != L[i].data);
	if (i) // 找到该元素
	{
		pre_e = L[j].data;
		return true;
	}
	return false;
}

8.后继
若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后继;否则操作失败,next_e无定义。用LocateElem函数来找到值为cur_e的元素的位置,如果cur_e不存在或者无后继,则返回错误。

bool NextElem(LinkList L, ElemType cur_e, ElemType& next_e)
{
	int j, i = LocateElem(L, cur_e); // 在L中查找第一个值为cur_e的元素的位置
	if (i) // L中存在元素cur_e
	{
		j = L[i].cur; // cur_e的后继的位置
		if (j) // cur_e有后继
		{
			next_e = L[j].data;
			return true; // cur_e元素有后继
		}
	}
	return false; // L不存在cur_e元素,cur_e元素无后继
}

9.插入
在L中第i个元素之前插入新的数据元素e。注意i的范围1<=i<length+1。l与k同步。
在这里插入图片描述

bool ListInsert(LinkList L, int i, ElemType e) {
	if (i<1 || i>ListLength(L) + 1)
		return false;
	int k = MAX_SIZE - 1;	 // k指向头结点
	int j = Malloc(L);	 // 申请新单元
	if (j) {	 // 申请成功
		L[j].data = e;	 // 赋值给新单元
		for (int l = 0; l < i-1; l++)	// 移动到第i-1个结点,k为第i-1个结点的位置
			k = L[k].cur;
		L[j].cur = L[k].cur;
		L[k].cur = j;
		return true;
	}
	return false;	//申请失败
}

10.删除
删除在L中第i个数据元素e,并返回其值。注意i的范围1<=i<length。j与k同步。在这里插入图片描述

bool ListDelete(LinkList L, int i, ElemType& e) {	
	if (i<1 || i>ListLength(L))
		return false;
	int k = MAX_SIZE - 1, j;	 // k指向头结点,j用来计数
	for (j = 0; j < i-1; j++)	// 移动到第i-1个结点,k为第i-1个结点的位置
		k = L[k].cur;
	j = L[k].cur;	//j用来存放释放元素位置
	L[k].cur = L[j].cur;
	e = L[j].data;
	Free(L, j);
	return true;
}

11.遍历

void ListTraverse(LinkList L,void (* vi)(ElemType n)) {
	int i = L[MAX_SIZE - 1].cur;	 // 指向第一个元素
	while (i) {		 // 没到静态链表尾
		vi(L[i].data);
		i=L[i].cur;	 // 指向下一个元素
	}
	printf("\n");
}

测试函数

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

void visit(ElemType n) {
	printf("%d ", n);
}

int main() {
	int i,j,k,e;
	LinkList L;
	InitList(L);	//初始化
	for (int j = 1; j <= 5; j++)
		ListInsert(L, 1, j);	//插入
	printf("在L的表头依次插入1~5后:L=");
	ListTraverse(L, visit);
	printf("L是否空:i=%d(1:是 0:否)表L的长度=%d\n", ListEmpty(L), ListLength(L));
	ClearList(L);
	printf("清空L后:L=");
	ListTraverse(L, visit);
	printf("L是否空:i=%d(1:是 0:否)表L的长度=%d\n", ListEmpty(L), ListLength(L));
	for (int j = 1; j <= 10; j++)
		ListInsert(L, j, j);	//插入
	printf("在L的表尾依次插入1~10后:L=");
	ListTraverse(L, visit);
	if (GetElem(L, 5, e))	//获取元素
		printf("第5个元素的值为%d\n", e);
	else
		printf("没有第5个元素\n");
	if (GetElem(L, 13, e))
		printf("第13个元素的值为%d\n", e);
	else
		printf("没有第13个元素\n");
	if (e = LocateElem(L, 8))	//定位元素
		printf("元素8的位置为%d\n", e);
	else
		printf("没有值为8的元素\n");
	if (e = LocateElem(L, 11))
		printf("元素11的位置为%d\n", e);
	else
		printf("没有值为11的元素\n");
	if (PriorElem(L, 10, e))	//前驱
		printf("元素10的前驱为%d\n", e);
	else
		printf("元素10无前驱\n");
	if (NextElem(L, 10, e))
		printf("元素10的后继为%d\n", e);
	else
		printf("元素10无后继\n");
	if (PriorElem(L, 1, e))		//后继
		printf("元素1的前驱为%d\n", e);
	else
		printf("元素1无前驱\n");
	if (NextElem(L, 1, e))
		printf("元素1的后继为%d\n", e);
	else
		printf("元素1无后继\n");
	ListTraverse(L, visit);	
	printf("表长为%d\n", ListLength(L));
	if (ListDelete(L, 4, e))	//删除
		printf("第4个元素为%d,已删除。\n", e);
	else
		printf("删除第4个元素失败(不存在此元素)。\n");
	printf("依次输出L的元素:");
	ListTraverse(L, visit); 
	return 0;
}

测试结果
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值