3.2.3 静态链表

概念

用数组描述的链表叫做静态链表,这种描述方法还有起名叫做游标实现法。

数组的元素由两个数据域组成,data和cur。数组的每个下标都对应一个data和一个cur。数据域data,用来存放数据
元素,也就是通常我们要处理的数据;而游标cur相当于单链表中的next指针,存放该元素的后继在数组中的下标。
存储结构:

#define MAXSIZE 1000 /* 存储空间初始分配量 */
typedef int Status; /* Status 是函数的类型,其值是函数结果状态代码,如 OK 等 */
typedef char ElemType;  /* ElemType 类型根据实际情况而定,这里假设为char*/
typedef struct
{
    ElemType data;
    int cur;    /* 游标(Cursor),为0时表示无指向*/
} Component,StaticLinkList[MAXSIZE];
/*这里定义了两个自定义数据类型Component和StaticLinkList,其中Component类型就是一个有两个int成员data和cur的
结构体。而StaticLinkList类型则是一个有1000个元素的结构体数组,这种数组的每个元素都是一个Component类型的结构体
*/

我们对数组第一个和最后一个元素作为特殊元素处理,不存数据。我们通常把未被使用的数组元素称为备用链表。而数组第一个元素,即下标为0的元素的cur就存放备用链表的第一个没有数据的结点的下标;而数组的最后一个元素的cur则存放第一个有数值的元素的下标,相当于单链表中的头结点作用,当整个链表为空时,则为0^2;存储数据的最后一个元素的下标为0。
逻辑图:
这里写图片描述

静态链表初始化代码:

Status InitList(StaticLinkList space)
{
    int i;
    for (i = 0; i < MAXSIZE - 1; i++)
        space[i].cur = i + 1;
    space[MAXSIZE - 1].cur = 0; /* 目前静态链表为空,最后一个元素的 cur 为 0 */
    return OK;

}

求静态链表长度代码:

/*  初始条件:静态链表 L 已存在。操作结果:返回 L 中数据元素个数 */
int ListLength(StaticLinkList L)
{
    int j=0;
    int i=L[MAXSIZE-1].cur;
    while(i)
    {
        i=L[i].cur;
        j++;
    }
    return j;
}

插入操作

为了辨明数组中哪些分量未被使用,解决的办法是将所有未被使用过的及已被删除的分量用游标链成一个备用的链表, 每当进行插入时,便可以从备用链表上取得第一个结点作为待插入的新结点。
代码:

/*  若备用空间链表非空,则返回分配的结点下标,否则返回 0 */
int Malloc_SSL(StaticLinkList space)
{
    int i;
    i = space[0].cur;/*  当前数组第一个元素的 cur 存的值 */
                    /*  就是要返回的第一个备用空闲的下标*/
    if (space[0].cur)//下标非零
        space[0].cur = space[i].cur;/*  由于要拿出一个分量来使用了, */
                                    /*  所以我们就得把它的下一个 */
                                    /*  分量用来做备用 */
    return i;
}

这段代码作用就是返回一个下标值,这个值就是数组头元素的cur存的第一个空闲的下标。从上面的图示例子来看,其实就是返回 7 。那么既然下标为 7 的分量准备要使用了,就得有接替者,所以就把分量 7 的cur值赋值给头元素,也就是把 8 给 space[0].cur,之后就可以继续分配新的空闲分量,实现类似 malloc ()函数的作用。

现在我们如果需要在“乙”和“”“丁”之间,插入一个值为”丙”的元素
插入逻辑图:
这里写图片描述
代码:

/*在 L 中第 i 个元素之前插入新的数据元素e*/
Status ListInsert(StaticLinkList L, int i, ElemType e)
{
    int k = MAXSIZE - 1;/*  注意 k 首先是最后一个元素的下标 */
    if (i<i || i>ListLength(L) + 1)//i>ListLength(L) + 1会出现中间未存储的变量。
        return ERROR;
    int j = Malloc_SSL(L);
    j = Malloc_SSL(L);  /*  获得空闲分量的下标 */
    if (j)
    {
        L[j].data = e;  /*  将数据赋值给此分量的 data */
        for (int l = 1; l <= i - 1; l++)/* 找到第 i 个元素之前的位置 */
            k = L[k].cur;
        L[j].cur = L[k].cur;/* 把第i个元素之前的cur 赋值给新元素的cur */
        L[k].cur = j;/* 把新元素的下标赋值给第i个元素之前元素的cur */
        return OK;
    }
    return ERROR;

}

解析:
• 当我们执行插入语句时,我们的目的是要在“乙”和”丁”之间插入”丙” 。调用代码时,输入 i 值为 3 。
• 第 4 行让 k=MAX_SIZE - 1=999 。
• 第 7 行, j=Malloc_SSL (L) 。此时下标为 0 的 cur 也因为 7 要被占用而更改备用链表的值为 8 .
• 第 12 -13 行, for 循环 l 由 1 到 2 ,执行两次。代码 k=L[k].cur; 使得 k=999,得到 k=L[999].cur=1,再得到 k=L[1].cur=2。
• 第 14行, L[j].cur = L[j].cur;因 j=7,而 k=2 得到 L[7].cur=L[2].cur=3。这就是刚才我说的让”丙”把它的cur 改为 3 的意思。
• 第 15 行,L[k].cur = j;意思就是 L[2].cur=7。也就是让”乙”得点好处,把它的cur改为指向”丙’的下标 7。
就这样,我们实现了在数组中,实王归之移动元素,却插入了数据的操作

删除操作

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

/*将下标为k的空闲结点回收到备用链表 */
void Free_SSL(StaticLinkList space, int k)
{
    space[k].cur = space[0].cur;    /*  把第一个元素的 cur 值赋给要删除的分量 cur */
    space[0].cur = k;   /*  把要删除的分量下标赋值给第一个元素的 cur */
}

意思就是“甲”现在要走,这个位置就空出来了,也就是,未来如果有新人来,最优先考虑这里,所以原来的第一个空位分量,即下标是 8 的分量,它降级了,把8给“甲”所在下标为 1 的分量的cur,也就是 space[1].cur=space[0].cur=8,而
space [0].cur=k=1, 其实就是让这个删除的位置成为第一个优先空位,把它存人第一个元素的 cur 中。
删除操作代码:

/*  删除在 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; 
    Free_SSL(L, j); 
    return OK;
}

有了刚才的基础,这段代码就很容易理解了。前面代码都一样,for 循环因为 i=1而不操作,j=k[999].cur=1,
L[k] .cur=L[j].cur=2 也就是 L[999].cur=L[1].cur=2。这其实就是告诉计算机现在 “甲”已经离开了,”乙”才是第一个元素。
逻辑图:
这里写图片描述

总结一下静态链表的优峡点

这里写图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值