什么叫静态链表?——用顺序表模拟链表,就叫做静态链表
第一列相当于数据域data,第二列相当于指针域next,
第一行(0)相当于头结点(头结点的数据域不放数据)
(a)图中0的next是1,1的next是2,……7的next是8,8的next是0,一遍下来又返回头结点
所以静态链表模拟的是——循环链表
这张图片中有一个姓氏是被删掉不存在也就是无效(之前说过数组中的数据域都是有数字的,只不过有些数字有效,有些数字无效,无效的数字在数组中就当做不存在)的,可以看出来是第7个ZHENG
所以数组中有一个有效数据长度length,length为几,这个数组的有效数据就是几
其余数据可能是0,也可能是其他数字,不过均无效
因为通过next的数字将每个格子串起来,发现串完一遍回到0后,7不在这串的里面(且指针域里面也没有7).
而在静态链表中,找空闲结点的时间复杂度为——O(n^2)。并不是O(n)
因为如果要看1是不是空闲结点,就要从头到尾串一遍,看看1在不在这个串里面(数组中的所有指针域里面有没有1出现过),同理2到n,每个都要遍历一遍,n个n遍为n方。
如果已知有一个空闲结点的情况下是遍历一次(所以有的说On),但若是有5000个结点,且不知道里面一共有几个空闲结点时,且第几个为空闲结点时,那就每个结点都需要遍历一遍看看在不在里面了。——时间复杂度取最坏的可能情况第5000个
其时间复杂度太大,但对于我们来说,我们使用链表最常用的操作是————头插尾插按值删。
前面指针类型的链表在头插尾插按值删的时候,每次插入我们都要malloc申请一个结点,每次删除我们要free释放p结点。(老是跟外部的空间也会有影响)
而静态链表是为了解决这个(频繁malloc申请free释放,造成内存的碎片化)麻烦的。
它一次给你总数固定的格子点数(不扩容的情况下),比如图中的给你10个点,然后你在里面插入删除
而在静态链表里面,要想插入,只要找到空闲结点O(n^2),将要插入的有效数据val赋值覆掉原来的无效数据O1,盖然后把它接入(先绑后,再接前)到有效数据链里面O1,就完成了。(时间复杂度为O(n^2))
要想删除,只要找到要删除的结点,然后把它从有效数据链里面踢出去,接入到无效空闲数据链里面,就完成了。
所以静态链表要改一下设计(为了缩小原来找空闲结点的时间复杂度),上图中a,b图就不对了
改造就是————把所有的空闲结点串起来,这样就找的快,
找空闲结点的时间复杂度就能降为O(1)
所以上图中就要划分为2张链表,一个链叫有效数据链,一个链叫空闲数据链,也就叫空闲链。
然后插入就是你去找你就从空闲链里拿出去,然后插入到有效链里。
然后规定,0号位置作为有效链的头结点,1号位置作为无效链的头结点,
那么该静态链表的结构设计要怎么做:看图写话
其内部成员就有,int data和int next
一个结点就是一个SNode即
MAXSIZE就是一个静态链表里面有多少对这样的
头文件中的函数声明
#pragma once
#define MAXSIZE 10
typedef struct SNode
{
int data;//数据域
int next;//后继指针,表示下一个结点的意思(实际上就是数组中的下标)
}SNode,SLinkList[MAXSIZE];
//SLinkList s;//s是一个长度为MAXSIZE的结构体数组,s指向该数组
//初始化
void InitList(SNode* ps);
//头插
bool Insert_head(SNode* ps, int val);
//尾插
bool Insert_tail(SNode* ps, int val);
//插入数据,在链表plsit的pos位置插入val数据元素,这个不写了,静态链表不考这么深
//判空
bool IsEmpty(SNode* ps);
//获取数据结点的个数
int Getlength(SNode* ps);
//在链表ps中 查找第一个key值,找到返回key值的结点下标,没有找到返回-1
int Search(SNode* ps, int key);
//删除链表ps中pos位置的值,删除跟插入一样成功或失败2种结果,这个也不写
//删除第一个val的值
bool DelVal(SNode* ps, int val);
//返回key的前驱地址,返回key的后继下标,这些也不写
//输出
void Show(SNode* ps);
//清空链表中的数据
void Clear(SNode* ps);
//销毁整个链表内存(交回)
void Destroy(SNode* ps);