数据结构与算法---链表(2)

本文介绍了静态链表的概念,它是如何使用数组模拟链表结构的,包括游标和备用链表的管理。通过C语言实现静态链表的初始化、插入和删除操作,并探讨了循环链表和双向链表的基本概念。这些数据结构在内存管理和操作效率上有其独特性,对于理解非指针环境下的链表实现具有启发意义。
摘要由CSDN通过智能技术生成
  • 静态链表

  • 静态链表定义

C语言具有指针能力,可以非常容易地操作内存中的地址和数据。但如早期的编程语言并没有指针,链表结构应该怎么样实现呢?

所以人们通过数组来代替指针进而描述单链表。通过让数组元素由data和cur两个数据域组成,也就是说每一个数组下标都对应一个data和cur。data域用来存放数据元素,cur则相当于单链表中的next指针,存放该元素的后继在数组中的下标,通常把cur叫做游标,这种用数组来描述单链表的方法称作游标实现法。

静态链表结点的构成

#define MaxSize 1000        //假设链表的最大长度为1000
typedef struct{
    ElemType data;
    int cur;                //游标,为0时表示无指向
}Component,StaticLinkList[MaxSize];

另外对于数组的第一个和最后一个元素作为特殊元素处理,不存数据。通常把未被使用的数组元素称为备用链表。备用链表的作用是回收数组中未使用或之前使用过(目前未使用)的存储空间,留待后期使用。也就是说,静态链表使用数组申请的物理空间中,存有两个链表,一条连接数据,另一条连接数组中未使用的空间。

Status InintList(StaticLinkList space){
    int i;                            //数组下标
    for(i = 0;i<MaxSize-1;i++)        //填充游标的数值
        space[i].cur = i+1;
    space[MaxSize-1].cur = 0;         //最后一个元素的游标为0
    return OK;                        //状态返回OK
}
  • 静态链表的插入

正如前面单链表中的操作所示,结点的申请和释放分别借用malloc和free两个函数来实现,但在静态链表中,操作的是数组并不存在动态的结点申请和释放问题,所以我们需要自己构建这两个函数,才可以做到插入和删除的操作。

插入的思路,便是每次插入时从备用链表中取得第一个结点作为待插入的新结点。

int Malloc_SLL(StaticLinkList space){
    int i = space[0].cur;
    if (space[0].cur){
        space[0].cur = space[i].cur;    //拿出下一个数组的游标赋值给数组下标为0的游标值
    }
    return i;
}

上面这段代码其实类似于malloc()函数的作用,分配了新的空闲分量。

所以在静态链表L中第i个元素之前插入新的数据元素e实现代码如下:

Status ListInsert(StaticLinkList L,int i, ElemType e){
    int i,j,k,l;
    k = MaxSize-1;                        //k是最后一个元素的下标
    if(i<1||i>ListLength(L)+l)
        return ERROR;
    j=Malloc_SLL(L);                      //获得空闲分量的下标
    if(j){
        L[j].data = e;                    //将数据赋值给data
        for(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;
}
  • 删除操作

和之前一样,我们需要自己实现free()函数。

void Free_SLL(StaticLinkList space,int k){
    space[k].cur = space[0].cur;    //把第一个元素的cur赋值给要删除的分量的cur
    space[0].cur = k;               //把要删除的分量下标赋值给第一个元素的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;
    L[k].cur = L[j].cur;
    Free_SLL(L,j);
    return OK;
}

静态链表在插入和删除时,只需要修改游标即可,但没有解决连续存储分配带来的表长难以确定的问题,这种没有使用指针的思维方式值得学习。

  • 循环列表

将单链表中终端结点的指针由空指针改为指向头结点,使整个单链表形成一个环,这种头尾相接的单链表称为单循环链表,简称循环链表

p->next=NULL是单链表,p->next=头结点是循环链表,构造上也与单链表相似。

  • 双向链表

双向链表是在单链表的每个结点中,再设置一个指向其前驱结点的指针域。

//线性表的双向链表存储结构
typedef struct DulNode{
    ElemType data;
    struct DulNode *prior;       //前继指针
    struct DulNode *next;        //后继指针
}DulNode,*DuLinkList;

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值