静态链表介绍

本文介绍了静态链表的概念,它是用数组模拟链表结构,通过游标而非指针实现。文章详细讲解了静态链表的定义、优点(兼容无指针语言、顺序表与链表优势)、初始化过程、增删查操作,并展示了如何处理备用链表和数据链表的关系。
摘要由CSDN通过智能技术生成

静态链表

文章目录

  • 静态链表
    • 静态链表是什么,有什么优点?
    • 代码部分
      • 初始化
      • 增删查的准备工作
      • 插入节点
      • 删除结点
      • 查找结点
      • 其他

静态链表是什么,有什么优点?

这里就不引用官方话术了,容易越来越混,静态链表叫链表,但它是一个结构体数组

每个结构体中有两个元素,放数据的和找数据的

这个“找数据的”在正常的单链表中体现为指针,而静态链表正是模拟了这一特性,用一个数组下标元素去找到下一个位置来访问数据,即游标,姑且可以称之为指针域,但并不是指针

给出定义也许能更清晰

typedef struct staticlist{
	int data;//数据域
	int cur;//“指针域”
}NODE,*LPNODE;

可以看到结构体中的两个元素均为整型变量,当然,这只是单个结构体的定义

那么用这个结构有什么优点呢?

  1. 我们知道,指针是c语言的灵魂,但大多数语言是没有指针的,那么静态链表的出现便很好的解决了这一问题,用游标来巧妙地实现链表以进行进一步操作
  2. 它兼备了顺序表和链式表的优点,数组实现链表下的O(1)时间复杂度它有,指针实现链表随意修改其内的值它也可以做到,但必须记住的是,静态链表仍然是以数组实现的,所以和寻常的顺序表一样需要提前规定最大存储量

可以说静态链表是顺序和链式表的小升级版

代码部分

还有很重要的一点,诚如上面谈到的,静态链表需要提前规定最大存储量,但与顺序表又有所不同,它的内部去按需分配的,没有存放数据的位置处于“沉睡”状态,拿着备用,只有当需要存储值时,才会把备用变为数据,这样实际上出现了两个链表,备用链表数据链表两大阵营

最开始分配的时候表内当然没有数据,这意味着什么呢?

在初始化静态链表时是先将其内部所有的元素初始化为沉睡状态,即一整条备用链表,来个图码结合感受一下

请添加图片描述

每一个结构体内的游标指向下一个结构体,以数组的下标来实现访问

初始化

#define MAX 100
LPNODE* InitList() {
	LPNODE *a = (LPNODE*)malloc(sizeof(LPNODE)*MAX);//对数组进行内存分配
	assert(a);
	for (int i = 0; i < MAX; i++) {
		a[i] = (LPNODE)malloc(sizeof(NODE));//对数组内的每个结构体进行内存分配
		assert(a[i]);
		a[i]->cur = i + 1;//规定a[0]->cur一般不变,可以看做头结点
	}//游标用来找到下一个结点,即数组的下标
	a[MAX - 1]->cur = 0;//我们规定当游标为0时备用链表的部分就结束
	return a;
}

当然看着写的挺复杂,别忘了,静态链表实质是一个结构体数组,一个结构体是一个结点,表最多有数组内结构体数量个结点,而每个结点内包含了放数据的和找数据的两部分

增删查的准备工作

备用链表好了,show一下数据链表的产生

int AllocNode(LPNODE *a) {
	int i = a[0]->cur;//始终记住cur是一个整型变量
	if (i == 0) {//数据链表和备用链表一样,都是游标到0表示结束
		return i;//这里是当下面所有的备用都存放了数据时采用的下下之策
    }			//返回0表示没有地方可以放了,要放只能把头结点也存了,这显然不现实
	a[0]->cur = a[i]->cur;
	return i;//返回一下角标的位置
}

这里其实就是和上面说到的一样,有一个数据拿一个备用过来,要一个给一点,按需分配,这个不一定要按着顺序申请下来,中间空一个空几个备用也是可以的,存储数组是随意的,只是代码中挨着分比较好理解,每次再找下一个可用结点时总是找一次就能找来用

这里来理解一下这个所谓的随意存储

基于上面一整条的备用链表,现在插一个数据3进去

请添加图片描述

可以看到当放入数据的时候,存数据的那个结点的游标变为了0,和代码中的注释是一个意思,数据链表的结点到0表示结束,而备用链表头结点中的游标则需要继续指向下一个备用处,它的结点也随之变化

下来不按代码中挨着插,随意插一个数据6进去

请添加图片描述

这次变的地方就多了,但其实还是那个道理,数据跟着数据,备用跟着备用,当数据从备用那抢了一个结点过来的时候也要和“老鹰捉小鸡”一样连着。同时备用也要保护好自己下面的备用,这个数据6是插到a[4]那去了,这就是所谓的“随意”,现在多少有点理解了吧,只是代码中为了操作方便挨着存

既然有AllocNode数据方从备用方抢一个结点,江湖规矩“有借有还” ,对应的自然有还结点的函数FreeNode

void FreeNode(LPNODE *a, int i) {
	(*a)->cur = a[0]->cur;
	a[0]->cur = i;
}

这个操作就简单多了,改一下角标就好了,让角标回到上一步的位置,这个还是基于上面那个借的代码的,如果是随意分配的,那就和单个线性表那边一样麻烦了

所以,抢来抢去有什么用呢 ,当然是用于熟悉的增删了

不过在这之前,抢和借的操作有了,备用链表有自己的大本营,即初始化的结果,那数据链表抢了结点放哪?So

int CreateList(LPNODE *a, int n) {
	int t = AllocNode(a);//结点拿到了,先变成自己的头结点有个着落
	int head = t;//数据链表也要有自己的头结点
	int m;
	for (int i = 1; i <= n; i++) {//有几个数据要几个结点
		m = AllocNode(a);
		scanf("%d",&a[m]->data);
		a[t]->cur = m;//放了数据后别忘了更新角标
		t = m;//每次都要记得保留一下最后角标的位置,以防出循环时漏了一个
	}
	a[t]->cur = 0;//让最后的角标变为0表示结束
	return head;//数据链表的头结点
}

给数据链表也来一个大本营,需要提醒的一点,这个“大本营”其实还是在最开始最开始那个结构体数组内,所以返回值只是一个头结点,依靠一个接一个角标构成整个“链表”,可以理解为数据链表是个病毒,不断感染备用链表的结点,它们始终是“一体”的

插入节点

双方已经就位了,插入结点实现如下

int InsertNode(LPNODE *a, int head, int i, int x) {//x是需要插入的数据
	if (i < 1) {//这个插入是在第i个结点的前面插入,所以0前面是不可能的,做一下处理
		printf("i is not experted\n");
		return 0;
	}
	int t =head;
	for (int j = 0; 0 != t && j < i - 1; j++) {
		t = a[t]->cur;
	}//从角标找一下插入的目标,遍历一遍
	if (0 == t) {
		printf("i is not found\n");
		return 0;
	}//找到末尾了也没找到,直接返回把
	int m = AllocNode(a);//要加一个数组肯定还是得先从备用那要一个结点
	if (0 != m){//万一抢到的结点是头结点就不好了
		a[m]->data = x;
		a[m]->cur = a[t]->cur;
		a[t]->cur = m;
		return 1;//成功的正常操作,依然以角标的更新为重点
	}
	else {
		return 0;
	}
}

别看这么一大串,其实办事的是从定义m的时候开始的,前面都是对特殊情况的处理

删除结点

int DeleteNode(LPNODE *a, int head, int i, int *tep) {
	if (i < 1) {//同上不述
		printf("i is not experted\n");
		return 0;
	}
	int t = head;
	for (int j = 0; j < i - 1 && 0 != a[t]->cur; j++) {//如果用t去与0作比较,退循环的时候就来不及了
		t = a[t]->cur;//所以这里采用角标去找
	}
	if (0 == a[t]->cur ){//同上不述
		printf("i is not found\n");
		return 0;
	}
    //从这往下就是正常的删除操作了
    //有一说一和双向链表迭代法换结点连去连去一样绕,建议多画图看一看,博主快被绕死了
	int m = a[t]->cur;
	a[t]->cur = a[m]->cur;
	*tep = a[m]->data;
	FreeNode(a, m);
	return 1;
}

真正删除的部分肯定不一样,特殊处理好像一样?

那你就大错特错了,在第一个for循环处其实是改了循环的条件的,如果依然按照之前插入时候的思路,会删到数组外面越界的

插入是对目标结点的前一个结点操作,而删除则是直接对目标结点操作,甚至会牵连到后一个结点,所以思路必须有所改变

查找结点

int LocateNode(LPNODE *a, int head, int x) {
	int t = a[head]->cur;
	while (a[t]->data != x && 0 != t) {
		t = a[t]->cur;
	}
	if (t == 0) {
		printf("x is not found\n");
		return 0;
	}
	return t;
}

这个真没什么好说的,想找到x这个值暴力遍历一遍就好了,没找到做一下处理即可

其他

同样销毁和改值这里略去,改就是查到改一下数据域就好了,销毁提个醒,要给每个结构体都释放内存,用个循环就好了,释放完了别忘了最外面的数组也要释放内存

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值