复习数据结构的第四天(静态链表)

上一周复习了双链表和循环链表,双链表比起单链表而言多了一个前驱指针,循环链表就是将单链表或者双链表原本指向空的末尾节点指向头节点从而形成循环。

静态链表的概念

在我看来,静态链表就像是顺序表和单链表的儿子,它既继承了顺序表要求系统分配一片连续的内存空间的性质,又继承了单链表每个节点存储一个数据和下一个节点地址的指针(这里的下一节点地址指在数组中的下标)。如同下面这个图:

静态链表的定义

不难看出,本质上来讲,静态链表就是一个结构体数组,每个节点拥有两个分量,一个是数据元素分量data,一个是指针分量,指向后继节点在数组中的位置。

#define Maxsize 10            //定义静态链表最大长度
typedef struct Node
{
    int data;   //数据域
    int next;    //下一节点的数组下标
}Slinklist[Maxsize];

最后一行的Slinklist[Maxsize]类似于我之前讲的链表类型Linklist,这里你可以理解为静态链表类型。

struct Node a[Maxsize] = Slinklist a

它们的意思都是定义了一个struct Node这种节点类型的一个数组。

静态链表的初始化

静态链表需要分配成两条链路,一条是已经插入数据的节点链路,一条是没有插入数据的空闲链路。与普通链表一样,静态链表同样存在带头结点和不带头结点的情况,但是为了方便,一般也是使用带头结点的静态链表。这里我将第0个节点作为节点链路的头结点。第1个节点作为空闲链路的头节点。

节点链路的最后一个节点的next定义为-1。空闲链路的最后一个节点的next定义为0。

void initsize(Slinklist L,int free)
{
    for(int i=1;i<Maxsize-1;i++)//定义空闲链表
    {
        L[i].next = i+1;       
    }
    L[Maxsize-1].next = 0;        //空闲链表结尾
    L[0].next = -1;                //节点链路结尾
}  

静态链表的插入和删除

插入的逻辑:

1、首先空闲链路从头节点开始查找有无空闲节点,若没有说明链表已满,有的话就分配一个节点给节点链路。

//分配空闲节点并返回索引
int allocate(staticList L) {  
    int i = L[1].next;  //指向空闲链表的头节点
    if(L[1].next!=0)    //如果空闲链表不为空
        L[1].next=L[i].next;  //头节点指针指向下一个空闲节点
    return i;  
}  

2、然后将空闲节点分配给节点链路,这里我使用的是头插法,可以联想一下单链表的头插法,将数据分配给空闲节点,然后改变节点链路的头节点的指针下标。

bool insert(staticList L,int data) {  
    int i = allocate(L);  //得到空闲节点的下标
    if (i == 0) {        //空闲节点下标为0说明没有空闲节点
        printf("申请空间失败\n");  
        return false;  
    }  
    L[i].data = data;    //插入数据
    
    L[i].next = L[0].next; //头插,新节点指向头节点的下一节点
    L[0].next = i;           //头节点的下一节点指向新节点
    return true;  
}  

 3、一直分配,直到空闲节点分配完

删除的逻辑:

bool delete(staticList L,int i)//删除下标为i的节点
{
    int m = L[0].next;      
    while(L[m].next != i&&L[m].next != -1)       //为了让m的指针指向删除节点
    {
        m = L[m].next;      
    }
    if(L[m].next == -1)            //找完了没有找到指向删除节点的指针
    {
        return false;
    }
    L[m].next = L[i].next;  //将删除节点的指针赋给找到的节点指针
    L[i].data = -1;
    L[i].next = L[1].next;  //回收节点
    L[1].next = i;
    return true;
} 

静态链表的查询

 查询的话跟单链表相似,按值查找只能从节点链路头节点开始遍历,我觉得这个没什么好说的。

总结

静态链表需要提前给它分配一定大小的内存空间,虽然它不像顺序表一样可以随机访问,但是它可以像链表一样插入和删除时不需要移动其他元素,只需要改变指针即可。

静态链表这一章我个人觉得没有必要太关注,感觉考的也比较少,而且它的实用性也很低,书上讲主要用于那些不能用指针的低级语言,有更好用的指针为什么不用😓,最主要的还是对这个知识点有大致的了解,知道是什么以及简单的操作就行了。

完整代码:

#include <stdio.h>  
#include <stdbool.h>  
  
#define MAXSIZE 10  
  
typedef struct Node {  
    int data;  
    int next; // 这里通常用于指向下一个节点的索引,但在静态链表中用于表示空闲状态  
}staticList[MAXSIZE];  
  
void init(staticList L) {  
    for (int i = 1; i < MAXSIZE - 1; i++) {  
        L[i].next = i + 1; //初始化空闲链表,指向下一个空闲位置 
    }  
    L[MAXSIZE - 1].next = 0; //空闲链表结束标识  
    L[0].next = -1;        //定义静态链表的头节点
}  
//分配空闲节点并返回索引
int allocate(staticList L) {  
    int i = L[1].next;  //获取空闲链表第一个元素下标
    if(L[1].next!=0)    //如果空闲链表不为空
        L[1].next=L[i].next;  
    return i;  
}  
//头插法实现插入 
bool insert(staticList L,int data) {  
    int i = allocate(L);  //得到空闲节点的下标
    if (i == 0) {        //空闲节点下标为0说明没有空闲节点
        printf("申请空间失败\n");  
        return false;  
    }  
    L[i].data = data;    //插入数据
    L[i].next = L[0].next;
    L[0].next = i;  
    return true;  
}  

bool delete(staticList L,int i)//删除下标为i的节点
{
    int m = L[0].next;      
    while(L[m].next != i&&L[m].next != -1)       //m的指针指向删除节点
    {
        m = L[m].next;      
    }
    if(L[m].next == -1)
    {
        return false;
    }
    L[m].next = L[i].next;  //将删除节点的指针赋给找到的节点指针
    L[i].data = -1;
    L[i].next = L[1].next;  //回收节点
    L[1].next = i;
    return true;
} 


int main() {  
    staticList L; 
    init(L);

    for (int i = 0; i < 7; i++) {  
        insert(L,i + 1);  
    }   
    int i =L[0].next;
    delete(L,2);
    while(i!=-1)            //遍历直到节点链路的最后一个节点
    {
        printf("%d\t",L[i].data);
        i = L[i].next;
    }
    return 0;  
}

  • 10
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值