大话数据结构(五)线性表相关概念——(静态链表、循环链表、双向链表)

前面一部分我们总结了线性表链式存储结构的单链表结构,主要要掌握单链表的查找、插入和删除操作。在之前的总结中我们发现,在单链表的插入操作中,我们需要动态地申请一个长度为size字节的连续空间,具体的操作是利用一个malloc函数,而在

删除操作中,我们调用了一个free()函数来释放内存空间。那么有没有办法不用这种形式来创建单链表呢,我们前辈就想出了用数组来代替指针来描述单链表。我们之前复习顺序存储结构的时候知道,线性表的顺序存储结构是用数组形式实现的,那么怎么又用数组实现链式存储结构呢,我们慢慢地深入。

目录

静态链表的定义

静态链表的插入操作

静态链表的删除操作

总结


静态链表的定义

为了更好的描述静态链表,我们先将静态链表的代码贴出来,然后再慢慢分析

/*线性表的静态链表存储结构*/
#define MAXSIZE 1000
typedef struct
{
    int data;//假定数据元素类型为int型
    int cur;//游标
}StaticList[MAXSIZE];

ok,现在我们写出了线性表的静态链表存储结构,第一点:我们可以看出这个结构体数组的长度非常长,达到了1000,这是因为我们不能动态地调整数组的大小,为了方便插入数据,我们通常会把数组建立地大一些。

第二点,我们看到结构体中定义了数组元素和一个游标,这个形式和单链表结构很相似,data对应于单链表中的数据域,而cur对应于单链表中指针域,用于存放指针。结构看上去和单链表没什么区别,那么我们继续看静态链表的初试化操作,代码如下:

/*初试化静态链表*/
int InitList(StaticLinkList space)
{
	int i;
	for(i = 0;i<MAXSIZE;i++)
	{
		space[i].cur = i+1;//指向下一个数据元素的游标		
	}
	space[MAXSIZE-1].cur = 0;//最后一个结点的游标为0
	return 1;//操作完成
}

好了,初试化完成,我们现在假设已经将想要的元素一次放进了数组中,我们用一个示意图来模拟一下静态链表的状态。

 

此时,a1的cur存的就是a2的下标2,a2的cur存的就是a3的下标3,最好一个a6的cur存储的是0表示之后就没数据了,返回第一个结点,而如果我们后面要继续存入数据的话,我们就通过第一个元素的cur找到现在下标7所在的空间是空闲的,可以存入。

静态链表的插入操作

现在我们来看看静态链表是如何进行插入操作的,我们从上面的示意图可以看到,静态链表中的数据元素依然是存储在内存单元中,相对于单链表结构中数据元素的存储地址是动态分配的,静态链表数据元素的的内存单元是连续的。因此,在静态链表中,我们操作的是数组,不存在动态链表的结点申请和释放问题。那么怎么模拟这种动态分配来做插入操作呢?诶,这个时候备用链表就派上用场了,当我们要插入数据元素的时候,我们只需要取用备用链表中的结点,然后修改它的下标为我们要插入的地方的下标,并且将该下标后继的下标cur依次增加1即可。那么我们现在编写实现算法的代码:

第一步,我们寻找到备用链表的第一个结点下标,并且将它的cur传递给头结点,这一步的目的就实现了动态分配的效果

int Malloc(StaticLinkList space)
{
	int i = space[0].cur;//指向第一个备用结点的下标
	if(space[0].cur)//如果备用链表非空
	space[0].cur = space[i].cur;
	return i;//返回第一个备用结点的下标
}

然后,我们实现插入操作,代码如下

int ListInsert(StaticLinkList L,int i,int e)
{
	int j,k,l;
	k = MAXSIZE - 1;//从后往前
	if(i<1 || i> ListInsert(L) + 1)
	{
		return 0;//操作失败
	}
	j = Malloc(L);
	if(j)
	{
		L[j].data = e;
		for(l = 1;l<i - 1;i++)
			k = L[k].cur;
		L[j].cur = L[k].cur;
		L[k] = j;
		return 1;//操作成功
	}
	return 0;
}

 

静态链表的删除操作

和前面一样,删除元素时,原来是需要释放结点的函数free(),现在我们也得自己实现它。

void Free(StaticLinkList space,int k)
{
    space[k].cur = space[0].cur;
    space[0],cur = k;
}

以上这段代码就实现了free()函数的功能,具体地来说就是将第一个备用结点的下标放入下标为k的下标中,然后将k传入头结点的游标中,是不是很拗口,就是说将我看做下标为k的结点,我现在空闲了,然后我现在要进入备用链表中,如果链表需要插入元素了,这个时候我就派上用场,头结点优先将我看做第一个备用结点,然后下一个备用结点就是我还没进入备用链表之前的第一个备用结点,如果还不明白多理解几遍就明白了。

删除操作的代码和插入的代码大同小异,具体实现如下:

int ListDelete(StaticLinkList L,int i)
{
	int j,k;
	if(i < 1 || i > ListLength(L))
		return 0;//不合法
	k = MAXSIZE - 1;
	for(j = 1;j <= i -1;j++)
		k = L[k].cur;
	j = L[k].cur;
	L[k].cur = L[j].cur;
	Free(L,j);
	return 1;
}

总结

静态链表的优点:

在插入和删除操作的时候,不需要大规模的移动数据,只需要修改相应元素的游标即可

静态链表的缺点:

虽然实现链表的结构,但是没有链表的“灵魂”,无法动态地分配数组。

发布了22 篇原创文章 · 获赞 2 · 访问量 491

猜你喜欢

转载自blog.csdn.net/weixin_42709632/article/details/101305999