3.线性表的链式存储结构————静态链表(C语言和C++完整解析)

目录

1. 静态链表的概念

2. 静态链表的存储结构

3. 静态链表的基本操作

4. 静态链表的编程实现

       4.1  C语言实现静态链表

       4.2  C++实现静态链表

       4.3测试结果

5. 静态链表的特点

6. 参考资料


1.静态链表的概念

因为有些语言没有指针,所以难以实现普通链表,静态链表就是用来解决这一问题的有力工具,静态链表使用数组来实现链表。静态链表用游标来代替普通链表的指针域,并且用下标代替普通链表的结点地址。

下面是一个静态链表的示意图

title=

结合上图来分析

(1)静态链表的每个结点包含三个要素:下标、数据、游标。下标表示当前元素所处的位置,类似于链表的结点地址;游标存放下一个有效结点的下标,类似于链表的指针域。例如下标为2的元素,存放的数据为B,游标值为3,说明下一个结点就是C。

(2)有效结点:含有数据的结点。

(3)第一个结点和最后一个结点不存放数据。第一个结点(下标为0)存放备用链表的第一个结点的下标;最后一个结点相当于链表的头结点,该结点的游标存放第一个有效结点的下标,当链表为空的时候,游标为0。其他结点的游标都存放下一个结点的下标。

(4)最后一个有效结点的游标为0,说明下一个结点为空。

 

2.静态链表的存储结构

使用结构体数组类型的数据来存储静态链表:

typedef struct
{
    ElemType data;//数据
    int cur;      //游标
}StaticLinkList[MAXSIZE];
//一维数组类型的定义方法:定义了一个元素类型为结构体,含有MAXSIZE个元素的数组类型StaticLinkList

3.静态链表的基本操作

(1)初始化:清空静态链表,建立一个空的静态链表。

(2)插入结点:在指定位置插入新结点。

(3)删除结点:删除指定位置的结点,并且回收该节点的存储空间以便下次再用。

(4)新建静态链表:将一系列元素放入空表。

(5)获取静态链表的长度:得到静态链表中有效元素的个数。

 

4.静态链表的编程实现

在程序的设计思路上,静态链表的新建、插入和删除等操作跟普通链表差不多,只需要注意静态链表的一些约定条件即可。

       4.1C语言实现静态链表

(1)静态链表的声明 staticlinklist.h

#ifndef STATICLINKLIST_H
#define STATICLINKLIST_H

#define MAXSIZE 1000
typedef int ElemType;
typedef enum Bool
{
    FALSE,TRUE//枚举默认值从0开始,依次加1
}Bool;

typedef struct
{
    ElemType data;//数据
    int cur;      //游标
}StaticLinkList[MAXSIZE];
//一维数组类型的定义方法:定义了一个元素类型为结构体,含有MAXSIZE个元素的数组类型StaticLinkList

Bool InitList(StaticLinkList L);//初始化静态表(也可以用来清空静态表)
int Malloc_SLL(StaticLinkList L);//申请新结点
Bool ListInsert(StaticLinkList L,int i,ElemType e);//指定位置插入新结点
Bool ListDelete(StaticLinkList L,int i,ElemType* e);//删除指定位置的结点
void Free_SLL(StaticLinkList L,int k);//将已删除的结点释放,方便下次再用
int ListLength(StaticLinkList L);//获取静态表的结点数目
void display(StaticLinkList L);//显示当前的顺序表
Bool CreatList(StaticLinkList L, int n);//用随机数新建一个静态表,其结点个数为n

#endif // STATICLINKLIST_H

(2) 静态链表的实现 staticlinklist.c

#include "staticlinklist.h"

Bool InitList(StaticLinkList L)
{
    int i;
    for(i=0;i<MAXSIZE-1;i++)
        L[i].cur=i+1;//第0个元素的游标指向第1个有效元素的下标,有效数据是从1开始的(第一个元素不存储信息)

    L[MAXSIZE-1].cur=0;
    return TRUE;
}

/*获取新节点
 *1.得到备用结点的下标
 *2.更新当前链表的第一个结点的游标
 */
int Malloc_SLL(StaticLinkList L)
{
    int i=L[0].cur;//获取备用结点的下标
    if(i)//备用链表不是空链表
        L[0].cur=L[i].cur;//更新备用链表头结点
    return i;
}

//将备用链表的第一个结点作为待插入的新结点
//插入方法:
//1.将新结点的下标值放入待插入位置的前一个结点的游标;
//2.新结点的游标值为,插入正确位置后,下一个结点的下标值。
Bool ListInsert(StaticLinkList L,int i,ElemType e)
{
    int j,k,l;

    k=MAXSIZE-1;//最后一个结点存放当前链表的起始下标
    if(i<1||i>ListLength(L)+1)//插入位置不合理
        return FALSE;

    j=Malloc_SLL(L);//新结点下标 
    if(j)//新节点不为空
    {
        L[j].data=e;
        for(l=0;l<i-1;l++)//这个下标值对应的结点的游标存放待插入位置的下标(包括第一个位置)
            k=L[k].cur;

        L[j].cur=L[k].cur;//新结点的游标存放原来处于第i个位置的结点下标值(即当前结点的游标)
        L[k].cur=j;//更新当前结点的游标值
        return TRUE;
    }
    return FALSE;
}

Bool ListDelete(StaticLinkList L,int i,ElemType* e)
{
    int j,k;

    k=MAXSIZE-1;//待删除的前一个结点下标
    if(i<1||i>ListLength(L))//删除位置不合理
        return FALSE;

    for(j=0;j<i-1;j++)//寻找删除点的下标值
        k=L[k].cur;

    j=L[k].cur;//待删除结点的下标
    L[k].cur=L[j].cur;//待删除结点的游标值(下个结点的位置)赋给待删除结点前一个结点的游标
    *e=L[j].data;
    Free_SLL(L,j);
    return TRUE;
}

void Free_SLL(StaticLinkList L,int k)
{
    L[k].cur=L[0].cur;//待删除结点作为新的备用链表头结点,他的游标为原来的头结点下标
    L[0].cur=k;//更新备用链表头结点的下标值
}

int ListLength(StaticLinkList L)
{
    int i=0,j;
    j=L[MAXSIZE-1].cur;//头结点的下标值
    while (j)
    {
        i++;
        j=L[j].cur;
    }
    return i;
}

void display(StaticLinkList L)
{
    int j;
    j=L[MAXSIZE-1].cur;//头结点的下标值
    if(j==0)
        printf("这是一个空的静态表");
    while (j)
    {
        printf("%d  ",L[j].data);
        j=L[j].cur;
    }
    printf("\n\n");
}

Bool CreatList(StaticLinkList L, int n)//新建一个静态表
{
    int i,j;

    if(n<1||n>MAXSIZE-2)//结点数目不合理
        return FALSE;

    if(L[MAXSIZE-1].cur)//当前静态表非空
        return FALSE;

    j=L[0].cur;//起始结点的下标
    for(i=1;i<n;i++)
    {
        L[j].data=rand()%100;
//        printf("新建元素%d  ",L[j].data);
        j=L[j].cur;
    }

    L[j].data=rand()%100+1;//最后一个元素
    L[j].cur=0;//最后一个元素的游标为0

    L[MAXSIZE-1].cur=L[0].cur;//最后一个结点存放当前链表的起始下标
    L[0].cur=j;//最后一个元素的下标
//    printf("新建元素%d  \n",L[i].data);
    return TRUE;
}

(3)测试程序 main.c

#include <stdio.h>
#include "staticlinklist.h"

int main()
{
    StaticLinkList List;
    int i;
    ElemType e;

    printf("            1.初始化静态链表\n");
    InitList(List);
    display(List);

    printf("\n            2.插入5个结点\n");
    for(i=0;i<5;i++)
    {
        printf("插入第%d个结点:%d\n",i+1,i);
        ListInsert(List,i+1,i);
        display(List);
    }

    printf("\n            3.删除5个结点\n");
    for(i=0;i<5;i++)
    {
        ListDelete(List,1,&e);
        printf("删除第%d个结点:%d\n",i+1,e);
        display(List);
    }


//    InitList(List);新建链表的时候不要求链表必须初始化,只要是空静态表就能新建
    printf("\n            4.随机创建10个结点\n");
    CreatList(List,10);
    display(List);

    printf("\n            5.清空静态表\n");
    InitList(List);
    display(List);

    return 0;
}

 

       4.2C++实现静态链表

(1)静态链表的声明  staticlinklist.h

#ifndef STATICLINKLIST_H
#define STATICLINKLIST_H

#define MAXSIZE 1000

template<typename ElemType>//用类模板来代替typedef int ElemType;
class staticLinkList
{
public:
    staticLinkList();//初始化静态表
    typedef struct
    {
        ElemType data;//数据
        int cur;      //游标
    }Node;

    int Malloc_SLL();//申请新结点
    bool ListInsert(int i,ElemType e);//指定位置插入新结点
    bool ListDelete(int i,ElemType* e);//删除指定位置的结点
    void Free_SLL(int k);//将已删除的结点释放,方便下次再用
    int ListLength();//获取静态表的结点数目
    void display();//显示当前的顺序表
    bool CreatList(ElemType a[],int n);//用随机数新建一个静态表,其结点个数为n
    void ListDelete();
private:
    Node L[MAXSIZE];

};

#endif // STATICLINKLIST_H

(2)静态链表的实现  staticlinklist.cpp

#include <iostream>
#include "staticlinklist.h"
using namespace std;

template<typename ElemType>
staticLinkList<ElemType>::staticLinkList()
{
    int i;
    for(i=0;i<MAXSIZE-1;i++)
        L[i].cur=i+1;//第0个元素的游标指向第1个有效元素的下标,有效数据是从1开始的(第一个元素不存储信息)
    L[MAXSIZE-1].cur=0;
}

template<typename ElemType>
int  staticLinkList<ElemType>::Malloc_SLL()
{
    int i=L[0].cur;//获取备用结点的下标
    if(i)//备用链表不是空链表
        L[0].cur=L[i].cur;//更新备用链表头结点
    return i;
}

template<typename ElemType>
bool staticLinkList<ElemType>::ListInsert(int i,ElemType e)
{
    int j,k,l;

    k=MAXSIZE-1;//最后一个结点存放当前链表的起始下标
    if(i<1||i>ListLength()+1)//插入位置不合理
        return  false;

    j=Malloc_SLL();//新结点下标
    if(j)//新节点不为空
    {
        L[j].data=e;
        for(l=0;l<i-1;l++)//这个下标值对应的结点的游标存放待插入位置的下标(包括第一个位置)
            k=L[k].cur;

        L[j].cur=L[k].cur;//新结点的游标存放原来处于第i个位置的结点下标值(即当前结点的游标)
        L[k].cur=j;//更新当前结点的游标值
        return  true;
    }
    return  false;
}

template<typename ElemType>
bool staticLinkList<ElemType>::ListDelete(int i,ElemType* e)
{
    int j,k;

    k=MAXSIZE-1;//待删除的前一个结点下标
    if(i<1||i>ListLength())//删除位置不合理
        return  false;

    for(j=0;j<i-1;j++)//寻找删除点的下标值
        k=L[k].cur;

    j=L[k].cur;//待删除结点的下标
    L[k].cur=L[j].cur;//待删除结点的游标值(下个结点的位置)赋给待删除结点前一个结点的游标
    *e=L[j].data;
    Free_SLL(j);
    return  true;
}

template<typename ElemType>
void staticLinkList<ElemType>::Free_SLL(int k)
{
    L[k].cur=L[0].cur;//待删除结点作为新的备用链表头结点,他的游标为原来的头结点下标
    L[0].cur=k;//更新备用链表头结点的下标值
}

template<typename ElemType>
int  staticLinkList<ElemType>::ListLength()
{
    int i=0,j;
    j=L[MAXSIZE-1].cur;//头结点的下标值
    while (j)
    {
        i++;
        j=L[j].cur;
    }
    return i;
}

template<typename ElemType>
void staticLinkList<ElemType>::display()
{
    int j;
    j=L[MAXSIZE-1].cur;//头结点的下标值
    if(j==0)
        printf("这是一个空的静态表");
    while (j)
    {
        cout<<L[j].data<<"  ";
        j=L[j].cur;
    }
    cout<<endl<<endl;
}

template<typename ElemType>
bool staticLinkList<ElemType>::CreatList(ElemType a[],int n)
{
    int i,j;

    if(n<1||n>MAXSIZE-2)//结点数目不合理
        return  false;

    if(L[MAXSIZE-1].cur)//当前静态表非空
        return  false;

    j=L[0].cur;//起始结点的下标
    for(i=0;i<n-1;i++)
    {
        L[j].data=a[i];
        j=L[j].cur;
    }

    L[j].data=a[i];//最后一个元素
    L[j].cur=0;//最后一个元素的游标为0

    L[MAXSIZE-1].cur=L[0].cur;//最后一个结点存放当前链表的起始下标
    L[0].cur=j;//最后一个元素的下标
    return  true;
}

template<typename ElemType>
void staticLinkList<ElemType>::ListDelete()
{
    int i;
    for(i=0;i<MAXSIZE-1;i++)
        L[i].cur=i+1;//第0个元素的游标指向第1个有效元素的下标,有效数据是从1开始的(第一个元素不存储信息)
    L[MAXSIZE-1].cur=0;
}

(3)测试程序  main.cpp

#include <iostream>
#include "staticlinklist.cpp"//类模板要求在编译的时候能同时访问模板定义和方法定义
using namespace std;

int main()
{
    staticLinkList<int> List;//模板类
    int i,e;

    cout<<"            1.初始化静态链表\n"<<endl;
    List.display();

    cout<<"\n            2.插入5个结点\n"<<endl;
    for(i=0;i<5;i++)
    {
        cout<<"插入第"<<i+1<<"个结点:"<<i<<endl;
        List.ListInsert(i+1,i);
        List.display();
    }

    cout<<"\n            3.删除5个结点\n"<<endl;
    for(i=0;i<5;i++)
    {
        List.ListDelete(1,&e);
        cout<<"删除第"<<i+1<<"个结点:"<<e<<endl;
        List.display();
    }

    cout<<"\n            4.新建静态表\n"<<endl;
    int a[10];
    for(i=0;i<10;i++)
        a[i]=rand()%100;
    List.CreatList(a,10);
    List.display();

    cout<<"\n            5.清空静态表\n"<<endl;
    List.ListDelete();
    List.display();

    return 0;
}

       4.3测试结果

5.静态链表的特点

优点:

与单链表类似,插入和删除的时候不需要移动大量元素;

缺点:

1.没有解决连续存储分配(数组)带来的表长难以确定的问题;

2.失去了顺序存储结构随机存取的特性。

6.参考资料

[1]小甲鱼的教学视频:https://www.bilibili.com/video/av2975983/?p=16

[2]部分图片的来源:https://www.2cto.com/kf/201512/453071.html

  • 16
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值