0
点赞
收藏
分享

微信扫一扫

4.数据结构学习之静态链表

追梦人的自留地 2022-05-03 阅读 116

1.静态链表的概述
我们了解线性表两种存储结构各自的特点,那么,是否存在一种存储结构,可以融合顺序表和链表各自的优点,从而 既能快速访问元素,又能快速增加或删除数据元素。

使用静态链表存储数据,数据全部存储在数组中(和顺序表一样),但存储位置是随机的,数据之间"一对一"的逻辑关系通过一个整形变量(称为"游标",和指针功能类似)维持(和链表类似)。

数据存放到数组中时,给各个数据元素配备一个整形变量,此变量用于指明各个元素的直接后继元素所在数组中的位置下标
在某些高级语言中,没有指针类型,所以想使用链表,得靠其它手段,比如静态链表
静态链表是顺序表和链表的结合,在初始化时申请一定大小的空间(可以等同于定义一定长度的数组),数组的元素是一个结构体变量,结构体内有两个元素,一是数据,二是游标(相当于链表中的指针),游标保存的是下一个节点的下标
静态链表内部有两个链表,数据链表和备用链表
数据链表和备用链表各有一个头节点(链表没有头节点操作非常不方便),因此如果申请10个节点的空间,能保存数据的仅有8个节点
一般情况下,第一个节点,也就是下标为0的元素,作为备用节点的头节点,最后一个元素,也就是最大下标的元素,作为数据链表的头节点
 

typedef struct {
    int data;//数据域
    int cur;//游标
}component;

静态链表的初始化

其实上面显示的静态链表还不够完整,静态链表中,除了数据本身通过游标组成的链表外,还需要有一条连接各个空闲位置的链表,称为备用链表。

备用链表的作用是:

回收数组中未使用或之前使用过(目前未使用)的存储空间,留待后期使用。

也就是说,静态链表使用数组申请的物理空间中,存有两个链表,一条连接数据,另一条连接数组中未使用的空间。通常,备用链表的表头位于数组下标为 0(a[0]) 的位置,而数据链表的表头位于数组下标为 1(a[1])的位置。

静态链表中设置备用链表的好处是:

可以清楚地知道数组中是否有空闲位置,以便数据链表添加新数据时使用。比如,若静态链表中数组下标为 0 的位置上存有数据,则证明数组已满。
 

void InitArr(component *array) {
    int i = 0;
    for (i = 0; i < maxSize; i++) {
        array[i].cur = i + 1;//将每个数组分量链接到一起
        array[i].data = 0;
    }
    array[maxSize - 1].cur = 0;//链表最后一个结点的游标值为0
}
int mallocArr(component * array) {
    //若备用链表非空,则返回分配的结点下标,否则返回 0(当分配最后一个结点时,
    //该结点的游标值为 0)
    int i = array[0].cur;
    if (array[0].cur) {
        array[0].cur = array[i].cur;
    }
    return i;
}
//向链表中插入数据,body表示链表的头结点在数组中的位置,add表示插入元素的位置,num表示要插入的数据
void insertArr(component * array, int body, int add, int num) {
    int tempBody = body;//tempBody做遍历结构体数组使用
    int i = 0, insert = 0;
    //找到要插入位置的上一个结点在数组中的位置
    for (i = 1; i < add; i++) {
        tempBody = array[tempBody].cur;
    }
    insert = mallocArr(array);//申请空间,准备插入
    array[insert].data = num;

    array[insert].cur = array[tempBody].cur;//新插入结点的游标等于其直接前驱结点的游标
    array[tempBody].cur = insert;//直接前驱结点的游标等于新插入结点所在数组中的下标
}
void freeArr(component * array, int k) {
    array[k].cur = array[0].cur;
    array[0].cur = k;
}
//删除结点函数,num表示被删除结点中数据域存放的数据
int deletArr(component * array, int num) {
    int tempBody =maxSize-1;
    int del = 0;
    int newbody = 0;
    //找到被删除结点的位置
    while (array[tempBody].data != num) {
        tempBody = array[tempBody].cur;
        //当tempBody为0时,表示链表遍历结束,说明链表中没有存储该数据的结点
        if (tempBody == 0) {
            printf("链表中没有此数据");
            return 0;
        }
    }
    //运行到此,证明有该结点
    del = tempBody;
    tempBody = maxSize-1;
    //删除首元结点,需要特殊考虑
    if (del == array[maxSize-1].cur) {
        newbody = array[del].cur;
        array[maxSize-1].cur=newbody; 
        freeArr(array, del);
    }
    else
    {
        //找到该结点的上一个结点,做删除操作
        while (array[tempBody].cur != del) {
            tempBody = array[tempBody].cur;
        }
        //将被删除结点的游标直接给被删除结点的上一个结点
        array[tempBody].cur = array[del].cur;
        //回收被摘除节点的空间
        freeArr(array, del);
    }  
}
void amendElem(component * array, int oldElem, int newElem) {
    int add = selectNum(array,oldElem);
    if (add == -1) {
        printf("无更改元素");
        return;
    }
    array[add].data = newElem;
}

int selectNum(component * array, int num) {
    //当游标值为0时,表示链表结束
     int tempBody = maxSize-1; 
    while (array[tempBody].cur != 0) {
        if (array[tempBody].data == num) {
            return tempBody;
        }
        tempBody = array[tempBody].cur;
    }
    //判断最后一个结点是否符合要求
    if (array[tempBody].data == num) {
        return tempBody;
    }
    return -1;//返回-1,表示在链表中没有找到该元素
}

以上就是基本函数定义

 

举报

相关推荐

0 条评论