• 数据结构与算法——线性表的顺序存储


    2.线性表

    线性表,顾名思义是像线一样性质的表,是最常用且最简单的一种数据结构。线性表的存储结构有顺序存储结构和链式存储结构两种。

    2.1 什么是线性表

    线性表是具有相同特性的数据元素组成的一个有限序列。

    线性表中元素的个数为线性表的长度,当元素个数为0时,称该线性表为空表,。

    线性表中的元素可以是整数、字符等简单数据,也可以由数个数据项组成的。

    每一种数据结构都有它自己的特征,线性表作为一种最简单的数据结构,有如下几个特征:

    1. 线性表中有且只有一个开始结点(头结点),这个开始结点没有前驱结点。
    2. 线性表中有且只有一个末尾结点(尾结点),这个末尾结点没有后继结点。
    3. 除去开始结点与末尾结点,其他结点都有一个前驱结点和一个后继结点。

    线性表在存储结构上有顺序存储结构和链式存储结构两种方式,但不管那种存储方式,他们的结构都有如下特点:

    1. 均匀性。虽然不同数据表的数据元素可以是各种各样的,但对于同一个线性表来说,数据元素必须具有相同的数据类型和数据长度。
    2. 有序性。各数元素在线性表中的位置只取决于它们的序号,数据元素之间的相对位置是线性的,即存在唯一的“第一个”和“最后一个”数据元素。除了第一个和最后一个外,其他元素前面均只有一个数据元素(直接前驱),后面均只有一个数据元素(直接后继)。

    线性表是一种比较灵活的数据结构,它的长度可根据需要增减,它也可以进行插入、删除等操作。可对线性表进行的基本操作如下:

    • 创建——Create():创建一个新的线性表。
    • 初始化——Init():初始化操作,将新创建的线性表初始化为空。
    • 获取长度——GetLength():获取线性表的长度。
    • 判断表是否为空——IsEmpty():判断线性表是否为空
    • 获取元素——Get():获取线性表某一个位置的元素。
    • 清空表——Clear():清空线性表,将线性表置为空。

    当然,在设计线性表时,也可以有其他操作,例如,根据某个条件对线性表中的元素进行排序,将两个线性表合并成一个线性表,重新复制一个线性表等,上面只是描述了线性表一些最基本的操作。

    2.2线性表的顺序存储

    2.2.1 顺序存储的原理

    所谓顺序存储,就是在存储器中分配一段连续的存储空间,逻辑上相邻的数据元素,其物理存储地址也是相邻的。

    地址序号
    0x001d0
    0x002b1
    0x003w2
    0x004t3

    要存储的4个字母被存储在一段连续的存储空间中。由存储空间的地址可以看出,这4个存储单元是连续的,第一个存储单元由序号0来标记,接下来的存储单元依次递增标记序号,这样在查找元素时,只要找到相应的索引就能找到元素,非常方便。线性表的这种存储方式称为顺序存储或顺序映射,又由于逻辑上相邻的两个元素在对应的顺序表中的存储位置也相邻,因此这种映射也称为直接映射。

    在顺序存储中,只要确定了线性表在存储空间里的起始位置,线性表中任意元素就都可随机存取,所以线性表的顺序存储结构是一种随机存取的结构。在高级语言中,顺序存储是用数组来实现的。

    在顺序存储中,系统不需要为表元素之间的逻辑关系增加额外的存储空间,而且在存取元素时,它可以根据给出的下标快速计算出元素的存储地址,从而达到随机读取的目的。例如如果要读取第3个元素,因它的下标为2(与第1个元素之间有两个间隔),由内存段的起始地址0x001和元素相差个数可以快谏计算出第3个元素的存储地址0x003,计算出地址后,就可以方便地对数据进行操作。

    但是,如果在顺序表中插入或删除元素,效率会特别低。对插入来说,只限于在表的长度小于表的容量的顺序表中,如果插入大量数据,很难保证空间是否充足而且一旦分配了存储空间,如果想要扩充容量,需要重新分配空间,然后将原来的数据复制到新的空间中,非常麻烦。另一方面,即使空间充足,在插入位置之后的元素也必须都要向后移动,效率非常低。同理,如果要删除大量元素,那么势必会造成空间的浪费,而且删除元素后,后面的元素都要向前移动,效率也会非常低。

    2.2.2顺序存储的实现

    1.创建顺序表

    在创建顺序表时,需要创建一个头结点来存放顺序表的长度、大小和地址等信息,然后再创建顺序表,同时将顺序表的地址保存在头结点中。

    实现思路:

    1. 定义一个struct 来保存顺序表信息
    2. 为头结点分配空间
    3. 为顺序表分配空间,将顺序表空间地址保存在头结点中。
    4. 将头结点地址分会给调用者。

    代码实现如下:

    typedef struct _tag_SeqList   //头结点,记录表的信息
    {
    
        int capacity;    //表容量
        int length;      //长度
        int *node;       //node[capacity]  ,为指针数组
        
    }TSeqList;
    
    SeqList *SeqList_Create(int capacity)	//返回值为SeqList*类型的,即顺序表的地址
    {
    	int ret;
        TSeqList * temp = NULL;
        temp = (TSeqList *)malloc(sizeof(TSeqList)) ;    //为头结点分配空间
        if(temp == NULL)
        {
        	ret = 1;
            printf("func SeqList_Create() error:%d\n",ret);
            return NULL;
    
        }
        memset(temp,0,sizeof(TSeqList));
        temp->capacity = capacity;
        temp->length = 0;
        temp->node = (int*)malloc(sizeof(void*)*capacity);    //分配一个指针数组
        
        if(temp->node == NULL)
        {
    		ret = 2;
            printf("func SeqList_Create() error:%d\n",ret);
            return NULL;
       
        }
        
        return temp;    
    }
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    2.求顺序表容量

    //求顺序表容量
    int SeqList_Capacity(SeqList *list)
    {
        TSeqList * temp = NULL;
        if(list == NULL)
        {
            retrun;
        }
    	temp = (TSeqList *)list;
        return temp->capacity;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    3.求顺序表长度

    //获取顺序表长度
    int SeqList_Length(SeqList *list)
    {
    	TSeqList *temp;
        if(list == NULL)
        {
    		return;   
        }
        temp = (TSeqList *)list;
        
        return temp->length;    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    4.插入元素

    增删改查是数据结构的核心操作,每种数据结构都要实现这几种最基本的操作。

    在顺序表中,如果要插入元素,则需要将插入位置后的所有元素向后移动。在插入过程中,需要考虑一些特殊情况:

    当顺序表已满时,表中的元素无法向后移动,需要作出特别处理(例如不插入,或者新开辟一块更大的空间来存储这些元素)。

    当插入的位置在空闲区域时,需要作出相应处理。例如插入元素导致顺序表的元素不连续,破坏了顺序表的存储规则,这时就要适当修改插入元素的位置。

    //插入元素
    int SeqList_Insert(SeqList *list,SeqListNode *node,int pos)  //参数为顺序列表地址、要插入的元素地址、插入位置
    {
    	int i;
        TSeqList *temp = NULL;
        if(list == NULL || node == NULL)
        {
    		return -1;
        }
        temp = (TSeqList *)list;
        if(temp->length >= temp->capacity)   //如果长度大于容量,则顺序表已满
        {
            return -2;
        }
        
        //容错
        if(pos > temp->length)    //如果给出的pos在长度后,即中间有空余
            pos = temp->length;   //就修正到最后一个元素后面
        
        for(i = temp->length; i > pos;i--)        //将插入位置的元素依次往后移动
        {
            temp->node[i] =temp->node[i-1];
        }
        temp->node[i] = (int)node;        //然后往腾出的位置插入新元素结点
        temp->length ++;                  //插入成功后,长度加1
        
        return 0;
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    5.删除元素

    从顺序表中删除某一个元素,则将元素删除后,需要将后面的元素依次向前移动来补齐空位。

    删除过程相对来说没有插入过程那么复杂,不必考虑内存不足的“溢出”问题但因为要移动元素,效率还是较低的。

    //删除元素
    SeqList * SeqList_Delete(SeqList * list,int pos)
    {
        int i;
        
        //先作健壮性判断
        TSeqList * tlist = NULL;
        SeqListNode * temp = NULL;
        tlist = (TSeqList *)list;
        if(list ==NULL || pos < 0 || pos >= tlist->capacity)
        {
            printf("SeqList_Delete () error\n");
            return NULL;   
        }
       
        temp = (SeqListNode *)tlist->node[pos];   //要删除的元素
        
        for(i = pos+1; i < tlist->length;i--)     //将删除元素位置后的所有元素向前移动
        {
            tlist->node[i-1] = tlist->node[i];
        }
        tlist->length--;                          //删除元素后,长度要减1
        return temp;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    6.查找某个位置上的元素

    在顺序表中查找某个元素是非常方便的,因为顺序表在底层是以数组来实现的,每个存储单元都有索引标注,要查找某个位置的上的元素,直接按索引来查找即可。

    //查找元素
    SeqList *SeqList_Get(SeqList *list,int pos)
    {
        //先作健壮性判断
        TSeqList *tlist = NULL;
        SeqListNode *temp = NULL;
        tlist = (TSeqList *)list;
        
        if(tlist == NULL || pos<0 || pos >= tlist->capacity)
        {
            printf("SeqList_Get() error\n");
            return NULL;
        }
        
    	temp = (SeqListNode *)tlist->node[pos];      //将表中的pos位置的结点指针赋给temp;
        return temp;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    7.清空表

    清空顺序表是将表中的内容分全部置0。

    //清空表
    void SeqList_Clear(SeqList *list)
    {
        TSeqList *temp;
        if(list == NULL)
        {
            return;
        }
        temp = (TSeqList *)list;
        temp->length = 0;
        memset(temp->node,0,(temp->capacity * sizeof(void *)));     //将顺序表全部归0
        return;
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    8.销毁表

    //销毁表
    void SeqList_Destory(SeqList *list)
    {
        TSeqList *temp = NULL;
        if(list == NULL)
        {
            return;
        }
        
        temp = (TSeqList * )list;
        
        if(temp->node != NULL)
        {
            free(temp->node);       //先释放头结点中的指针数组
        }
        free(temp);                 //再释放头结点
        return;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
  • 相关阅读:
    C语言学习系列-->一篇带你看懂内存函数
    带你深入了解什么是 Java 线程池技术
    MySQL备份测试
    Classloader隔离技术在业务监控中的应用
    spark
    利用gpt进行GMV变化数据分析
    ThreadLocal类
    计算机毕业论文选题推荐|软件工程|系列八
    linux 压缩命令
    小米手机怎么识别图片上的表格文字?
  • 原文地址:https://blog.csdn.net/weixin_52190799/article/details/126075107