首页 > 代码库 > 05.线性表(四)链式存储结构.静态链表

05.线性表(四)链式存储结构.静态链表

链式存储结构.静态链表   
     
一、静态链表
1.静态链表存储结构
    单链表是通过指针实现的,但是我们也可以通过数组来代替指针描述单链表,即静态链表如何实现静态链表?构造数组的元素由两个数据域组成:data和cur,即数组的每个下标都对应一个data和一个cur。
数据域data:用来存放数据元素,即要处理的数据;
游标cur:存放该元素的后继在数组中的下标,相当于单链表中的next指针;
为了方便插入数据,我们通常会把数组建立得大一些,以便有一些空闲空间而不致于出现溢出情况。
线性表的静态链表存储结构
#define MAXSIZE 1000    //假设链表的长度为1000(个元素)
typedef struct
{
    ElemType data;    //数据域,int类型
    int cur;          //游标(Cursor),为0时表示无指向
}Component,StaticLinkList(MAXSIZE);
2.备用链表
    由于数组的第一个和最后一个元素作为特殊元素处理,不存数据,因此我们把未使用的数组元素称为备用链表。
因此,我们规定:
(1)数组第一个元素(即下标为0的元素)的游标cur存放第一个空闲空间元素的下标(备用链表的第一个元素)
(2)数组最后一个元素的游标cur存放第一个有数值的元素的下标(相当于单链表中的头结点作用)。当整个链表为空时则最后一个元素的游标cur为0。
(3)链表的最后一个有值元素的cur为0
技术分享
技术分享

升华笔记:如何将一维数组list中各分量链成一个备用链表?
typedef  int Status
Status InitList(StaticLinkList list)
{
    int i;        //i为数组下标,MAXSIZE为链表长度
    for(i=0;i<MAXSIZE-1;i++)
    {
        list[i].cur=i+1;    // 将数组第一个元素的游标cur指向备用链表的第一个结点存储位置(数组下标),依次类推  
    }
    list[MAXSIZE-1].cur=0;    //目前静态链表为空,最后一个有值元素的cur为0   
}

二、静态链表的插入/删除操作
    静态链表的插入和删除操作,最关键是要解决如何用静态模拟动态链表结构的存储空间的分配,需要时申请,无用时释放。
1.静态链表的插入操作
(1)算法思路
    为了辨明数组中哪些分量未被使用,解决的办法是将所有违背使用过的及已被删除的分量用游标cur链成一个备用的链表(即空链表),每当进行插入时,便可以从备用链表上取得第一个结点(即未被使用的第一个结点)最为待插入新结点
实现获取空闲分量下标Malloc_SLL函数算法:
a.获取数组第一个元素的游标cur=i,其存放的是备用链表的第一个空闲结点;
b.将数组第i个元素的游标cur=i+1赋值给头指针
c.返回被使用的数组元素下标
 int i=list[0].cur;         //如i=list[0].cur=7
 list[0].cur=list[i].cur; //头指针list[0].cur=list[7].cur=8
 return i;
(2)源码实现
/*1.若备用空间链表为空,则返回分配的结点下标,否则返回0*/
int Malloc_SLL(StaticLinkList list)
{
    int i=list[0].cur;        //获取备用链表的第一个结点下标(当前数组第一个元素的cur存储第一个备用空闲的下标)
    if(list[0].cur)            //如果list[0].cur!=0,则说明数组含有非空元素
    {
            list[0].cur=list[i].cur;    //由于要拿出一个备用链表的结点使用,我们需要将数组第一个元素的cur存放下一个空出来的元素作备用
    }
    return i;//返回被使用的下标
}
//注释:假如先前list[0].cur=7(数组下标值),当下标为7的分量(数组元素)准备被使用了,就得有接替者,所以把分量7(list[i].cur,其中i=7)的cur值=8,赋值给头元素(list[0].cur),之后就可以继续分配新的空闲分量。//
/*2.在L中第i个元素之前插入新的数据元素e*/
typedef int Status
typedef int ElemType
Status ListInsert(StaticLinkList L,int i,ElemType e)
{
    int j,k,m;
    k=MAX_SIZE-1;    //注意:k首先是最后一个元素的下标    
    if(j<1 || j>ListLength(L)+1)
            return ERROR;
    j=Malloc_SLL(L);        //a.获得空闲分量的下标
    if(j)
    {
         L[j].data=http://www.mamicode.com/e; //b.将数据赋值给此分量的data
         for(m=1;m<i-1;i++)  //c.找到第i个元素之前的位置,将数组最后一个元素的cur(=1)存到变量k
            k=L[k].cur;    //k=1
        L[j].cur=L[k].cur;    //将第i个元素之前的cur赋值给新元素的cur
        L[k].cur=j;          //把新元素的下标赋值给第i个元素
        return OK;
    }
    return ERROR;
}
注释:第i个元素,是指链表的第i个元素,非数组下标存储位置。
             i-插入静态链表位置;j-存储空间空闲位置;
技术分享

2.静态链表的删除操作
源码实现
/*1.将下标为k的空闲结点回收到备用链表*/
void Free_SSL(StaticLinkList space,int k)
{
    space[k].cur=space[0].cur;    //将数据的第一个元素cur(其值为备用链表的第一个空闲元素下标),赋值给要删除分量的cur
    space[0].cur=k;    //把要删除的分量下标赋值给第一个元素的cur
}
/*2.删除在L中第i个数据元素e*/
typedef int Status
Status ListDelete(StaticLinkList L,int i)
{
    int i,k;
    if(i<1 || i>ListLength(L))
            return ERROR;
    k=MAXSIZE-1;            //存储链表最后一个元素的下标
    for(j=1;j<=i-1;j++)
            k=L[k].cur;            //找到要删除元素的前一个元素,并将其cur值赋值给k(即要删除元素的下标)
    j=L[k].cur;                     //将删除元素的游标值赋值给j,即值为下一个元素的下标
    L[k].cur=L[j].cur;            
    Free_SSL(L,j);
}
/*3.初始条件:静态链表L已经存在。操作结果:返回L中数据元素个数*/
int ListLength(StaticLinkList L)
{
    int j=0;
    int i=L[MAXSIZE-1].cur;
    while(i)
    {
        i=L[i].cur;
        j++;
    }
    return j;
}

三、静态链表的优缺点
1.优点
    在插入和删除操作时只需要修改游标,不需要移动元素,从而改进了在顺序存储结构中的插入和删除操作需要移动大量元素的缺点;
2.缺点
(1)没有解决连续存储分配带来的表长度难以确定的问题;
(2)失去了顺序存储结构随机存取的特性;


05.线性表(四)链式存储结构.静态链表