数据结构学习笔记——静态链表

古老的链表——静态链表
定义:用数组描述的链表叫做静态链表
下面是静态链表的定义及一些基本操作:
1.定义节点

#define MAXSIZE 1000
typedef struct
{
 ElemType data;
 int cur;
}Component,StaticLinkList[MAXSIZE];

解析:
①静态链表也有数据域和指针域,但这里的指针并不是真正的指针,而是用cur来代替,cur存放该元素的后继在数组元素当中的下标,因此cur也叫游标。
②注意到在最后的

}Component,StaticLinkList[MAXSIZE];

这里的StaticLinkList就相当于之前的LinkList,它这样定义了之后就可以用来定义此数据类型的指针。因为StaticLinkList[MAXSIZE]其实就相当于 *StaticLinkList;例如指针当中 int a[100],int a[ ],都是和 int * a;一个意思,a都是一个一级指针。但可能不同的是如果有int *p;对于前两个 a,不能有a=p;可以是p=a;而最后一个a对于两个语句都可以。
2.创建静态链表

Status InitList(StaticLinkList space)
{
 int i;
 for(i=0;i<MAXSIZE-1;i++)
 {
  space[i].cur=i+1;
 }
 space[MAXSIZE-1].cur=0;
 return OK;
}

说明:
①这里就把静态链表的基本结构创建好了,就是每一个节点的cur存放的是下一个节点在数组中的下标。
②关于静态链表,还有更重要的特征,在真实存放数据时需要构造:对于静态链表,通常把未被使用的数组元素称为备用链表,而数组第一个元素,即下标为0的元素的cur就存放备用链表第一个节点的下标;而数组中的最后一个元素的cur则存放第一个有数值的元素的下标,它相当于起到了单链表中的头结点的作用。
下面将上面初始化的静态链表存入一些数值:

Status CreatList(StaticLinkList space)
{
int i;
srand(time(0));
for(i=0;i<7;i++)
{
 space[i].data=rand()%1000+1;
}
space[0].cur=7;
space[6].cur=0;
space[MAXSIZE].cur=1;
}

解析:
①首先可以知道这里data存入数值的元素下标为1~6,因为使用的是随机数,所以为了方便描述,把这六个元素分别命名为甲,乙,丙,丁,戊,己,庚;
②此时甲这里就存有下一个元素乙的下标2,乙就存有下一个元素丁的下标3,以此类推,一直到丁存放了下一个元素庚的下标6;
③但是庚是最后一个有值元素,这里为了后面对此链表进行一些操作,需要将这最后一个有值元素的游标值定为0;就像单链表当中最后一个节点的指针域就是赋值为0 ;
④而整个表最后一个元素,也就是space[999]的游标值就应该是甲的下标1,因为前面说过,数组的最后一个元素的cur需要存放第一个有数值元素的下标,它是需要起到头结点的作用的。
⑤而space[0]的cur需要存放的是备用空间第一个元素下标,由于在上面的循环中为元素data复制的只有下标为1~6的元素,所以就有space[0].cur=7;
3.静态链表的插入操作:

//静态链表的插入操作
int Malloc SLL(StaticLinkList space)
{
int i=space[0].cur;
if((space[0].cur)!=0)
{
 space[0].cur=space[i].cur;
}
return i;
}
//在L中第i个有值元素之前插入新的元素,并为新元素的数据域赋值 
Status ListInsert(StaticLinkList L,int i,ElemType e)
{
int j,k,l;
k=MAXSIZE-1;
if(i<1||i>ListLength(L)+1);
{
 return ERROR;
}
j=Malloc SSL(L);
if(j!=0)
{
 L[j].data=e;
 for(l=1;l<=i-1;i++)
 {
  k=L[k].cur;
 }
 L[j].cur=L[k].cur;
 L[k].cur=j;
 return OK; 
}
else return ERROR;
} 

解析:
①第一步:建立分配新节点的函数:
第一句:

int i=space[0].cur;

利用的是静态链表的性质:space[0].cur=备用链表第一个元素下标。此时这种从备用链表上取得第一个节点作为待插入的新节点的操作相当于指针链表中利用malloc函数为新的节点分配空间的操作。
②注意下面if的判断语句:

 if((space[0].cur)!=0)

表示下面的语句是只有当备用链表的第一个节点的下标不是0才能进行;其实就是表示备用链表不能为空;什么叫备用链表为空呢?
注意:当数组中除下标为0的space[0]当中的data没有值之外,其他的都是有值的,此时的space[0].cur=0;所以这个语句表示的含义就是备用链表不能为空,否则当然不能实现插入。
③:然后就是需要将新的备用链表的第一个元素的下标赋值给space[0].cur,这样当然是保证链表的完整性。

space[0].cur=space[i].cur;

④插入函数:
(1)首先还是要注意插入的位置的正确性;于是有判断语句:

 if(i<1||i>ListLength(L)+1);
 {
  return ERROR;
 }

还是想指针链表一样插入的位置不能是第0个,只能是第1个,不能是第ListLength(L)+2个,最多是第ListLength(L)+1个。
(2)带指针参数L获得空闲节点下标:

j=Malloc SSL(L);

(3)接下来就是具体的核心插入步骤:

L[j].data=e;
  for(l=1;l<=i-1;i++)
  {
   k=L[k].cur;
  }
  L[j].cur=L[k].cur;
  L[k].cur=j;

很容易看懂,但需要注意的是这里插入的位置不是整个数组的第i个位置,而是有值链表中的第i个位置。
(4)下面引入静态链表满的问题;如果在上面的数组当中有999个值都存放在了下标为1~999的节点中,则静态链表满:
问题:1.此时space[0]当中的data中有无值?
2.此时space[999].cur应当存多少,是应当遵循最后一个有值元素的cur值应为0;还是应当遵循最后一个元素的cur值应为第一个有值元素的下标?
3.此时的space[0].cur应该为0,这样在之前的插入当中才能使用判断语句: if((space[0].cur)!=0) 但是从原则上来讲space[0].cur应当等于第一个备用链表的第一个数组元素的下标,但是这里没有了备用链表,那到底该怎样理解这个0呢?是不是就该把space[0]理解为最后的一个备用链表呢?那么它的data可不可以赋值呢?
4.做一个猜想:
(1)假设把space[999].cur赋值为0,那么它类似于一个单链表,但没有头节点,它的最后一个节点的指针域是赋值为0的,那么在循环中就可以加上(space[i].cur!=0)这样的判断句,进而可以对链表进行需要的操作。
(2)假设把space[999].cur赋值为1,那么它就相当于一个循环链表!
5.其实上面的插入程序中有一个问题:

 space[0].cur=space[i].cur;

这条语句是分配空闲节点的,但如果空余节点是space[999],那么执行这条语句的结果就是:

space[0].cur=space[999].cur

但是space[999].cur的值为1,是第一个有值元素的下标,那么就相当于新的备用空间的第一个元素是第一个有值元素!很明显这是矛盾的。
(5)介于上面的(4)中提出的问题,现在终于有了正确的解释:
首先,对于一个静态链表,它需要满足的规律还有很重要的两点:
①对于这个数组的第一个元素和最后一个元素需要做它输处理,它们是不存数据的。
②space[0].cur在常规下情况下,它的值为第一个备用链表的第一个元素的下标,但当备用链表为空的时候,它的值就会被赋值为0.
③space[999].cur的值为第一个有值元素的下标,起到头结点的作用。
④最后一个有值元素的cur值为0,也是确定的。
4.静态链表的删除操作:

//静态链表的删除操作:
//删除在L中的第i个元素
Status ListDelete(StaticLinkList L,int i)
{
int j,k;
if(i<1||i>ListLength(L))
{
return ERROR;
}
k=MAXSIZE-1;
for(j=1;j<=i-1;j++)
{
k=L[k].cur;
}
j=L[k].cur;
L[k].cur=L[i].cur;
Free_SSL(L,j);
return OK;
}
void Free_SSL(StaticLinkList space,int k)
{
space[k].cur=space[0].cur;
space[0].cur=k;
} 

解析:
①这里还是需要用到这样的两个函数,第二个函数较为容易理解,就是手动实现释放分配的空间。具体的实现思路不详述。
②这里具体说一下第一个函数中实现删除的具体方法:

 k=MAXSIZE-1;
for(j=1;j<=i-1;j++)
{
k=L[k].cur;
}

首先:这个循环的目的是使得k的值最终为i-2;因为j=1时,k=1(此时已进入循环并实现了第一步);j=2时,k=2;这样以此类推,当最后一次循环执行时,j=i-1,则k=i-1;
然后带进下面的语句:
j=L[i-1].cur; 相当于j=i;
L[i-1].cur=L[i].cur;
最后将j也就是i带入之前的释放空间函数,就实现了将第i个有值元素的删除,并且将删除的元素所占用的空间实现了回收。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值