静态链表基础

静态链表

在没有指针和类的早期编程语言(如Basic、Fortran等语言),为了实现链表,有人想到了拿数组来代替指针,来描述单链表。

首先,我们让数组的元素都是由两个数据域组成,data和cur,也就说,数组的每个下标都对应了一个data和cur,而cur相当于单链表中的next指针,
存放该元素后继在数组中的下标,我们把cur叫做游标。我们把上述这种数组描述的链表叫做静态链表,这种描述方法叫做游标实现法

为了方便我们插入数据,我们通常会把数组建立的大一些,以便有一些空闲空间以至于插入时不会溢出。另外,我们对数组第一个和最后一个元素作为特殊元素处理,不存数据,我们通常把未被使用的数组元素称为备用链表

数组的第一个元素,即下标为0的元素的cur就存放备用链表的第一个结点的下标;而数组的最后一个元素的cur则存放第一个有数值元素的下标,相当于单链表的头结点作用。


静态链表的结构

typedef struct {
    ElemType data; //数据域
    int cur; //游标
}Component, StaticLinkList[MAXSIZE];

初始化静态链表

bool InitList(StaticLinkList & sapce)
{
    for(int i = 0; i < MAXN - 1; ++i)
        space[i].cur = i + 1;
    space[MAXN - 1].cur = 0; //目前静态链表为空,最后一个元素的cur为0
    return true;
}

静态链表的插入操作

静态链表中要解决的是:如何用静态模拟动态链表结构的存储空间的分配,需要时申请,无用时释放。

在动态链表中,结点申请和是否别分借用malloc()free()两个函数来实现,在静态链表中,操作的是数组,不存在像动态链表中的结点申请和释放问题,所以我们需要自己实现这两个函数。

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

手动实现Malloc_SLL()函数:

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

静态链表的插入

在第i个位置插入新的元素e:

bool ListInsert(StaticLinkList &L, int i, ElemType e)
{
    int k = MAXN - 1; //k是最后一个元素的下标
    if(i < 1 || i > ListLength(L) + 1) return false;  //插入位置非法
    int j = Malloc_SLL(L);  //分配一个空闲下标
    if(j) {
        L[j].data = e; //将数据赋值给此分量的data域
        for(int p = 1; p <= i - 1; ++p)  //找到第i个元素之前的位置
            k = L[k].cur; 
        L[j].cur = L[k].cur;  //把第i个元素之前的cur赋值给
        L[k].cur = j;   //把新元素的下标赋值给第i个元素之前元素的cur
        return true;
    }
    return false;
}

静态链表的删除

手动实现Free_SLL()函数:

void Free_SLL(StaticLinkList &space, int k)
{
    space[k].cur = space[0].cur; //把第一个元素cur值赋值给要删除的分量cur
    space[0].cur = k;  //把要删除的分量下标赋值给第一个元素的cur
}

删除静态链表第i个元素的算法:

bool ListDetele(StaticLinkList &L, int i)
{
    int k, j;
    if(i < 1 || i > ListLength(L)) return false;
    int k = MAXN - 1;
    for(j = 1; j <= i - 1; ++j) //找到要删除结点的前驱
        k = L[k].cur;
    j = L[k].cur; // 要删除的结点
    L[k].cur = L[j].cur; //让要删除结点的前驱指向删除结点的后继。
    Free_SLL(L, j); //删除第j个位置的结点

    return true;
}

总结

静态链表的优点: 在插入和删除操作时,只需要修改游标,不需要移动元素,从而改进了在顺序存储结构中的插入和删除操作需要移动大量元素的缺点。

静态链表的缺点: 没有解决连续分配内存带来的表长难以确定的问题,失去了顺序存储结构随机存取的特性。

总的来说,静态链表是为了给没有指针和类的高级语言设计的一种实现单链表能力的方法,尽管很难用到,但应理解其思想,以备不时之需。

posted @ 2018-12-29 12:23 schrodingercatss 阅读(...) 评论(...) 编辑 收藏