数据结构——顺序表(C语言版)

顺序表的基本概念:

            顺序表(Sequential List)是一种基本的数据结构,用于存储线性表(元素之间有序排列的数据集合)。顺序表的特点是元素在内存中连续存储,每个元素占用固定大小的存储单元,通过元素在内存中的相对位置来确定其逻辑顺序。从概念中可以看出顺序表和数组是很相似的

        顺序表与数组的区别:

               顺序表与数组的相同点都是在内存中连续存储,但顺序表并非是数组,只是顺序表的底层和数组一直

           

这里简单画了一个图,我们将数组看作是一个普通衣柜,顺序表看作是一个折叠衣柜。因为数组的大小在一开始设定的时候就是固定的,就像普通衣柜一样,在厂家制作时候大小就已经定死了,而顺序表就像折叠衣柜一样,可以根据数据的多少来调节顺序表的大小,就像根据自己的衣服量将衣柜调节到合适的大小。

顺序表的分类:

        静态顺序表:

                

        上图是一个静态顺序表的代码,定义了一个名为SeqList(顺序表)的结构体,里面存放着一个名为SLDatatype类型的data数组以及一个size,那么这里用typedef是因为后续如果需要修改顺序表里数组的类型会很方便。而size的作用为记录顺序表的大小。

        可以看出静态顺序表与数组基本相同,他们都拥有着无法调节大小的缺点。

      动态顺序表:

        

上图是一个动态顺序表,那么可以看出与静态的区别是将固定的data数组变成了一个指针,以及多了一个capaticy的变量。

         从图可以很清楚的看出,data指针是用来指向一块可用空间,size表示有效数据的长度,capaticy表示可用容量。当数值大小等于容量的时候可以使用realloc进行扩容,解决了静态数组容量不足或者需要动态调整的问题。

顺序表的实现:

        初始化和销毁 :

                初始化顺序表:

         

          

        在SeqListInit函数中我们的参数为SeqList* ps,用来指向原顺序表。开始的时候我们将

SLDateType* a的指针指向NULL,并将capacity以及size都初始化为0。

        销毁顺序表:

        

        销毁顺序表时,因为我们指针a指向的空间是由realloc申请来的,所以需要进行free(释放),来避免内存泄漏,并重新将capacity以及size都初始化为0。

        扩容:

        当size(有效数据的容量)==capaticy(总容量)时候就需要进行扩容,

        newcapaticy = ps->capacity==0?4:ps->capacity*2;这段代码的意思是如果capaticy的容量为0

时,则返回数值4,如果不为0那么将capaticy*2,给newcapaticy。那么在这里可能会有些疑惑为什么是capaticy*2而不是一个一个的增加或者一下子给个更大的值。要知道realloc每次去向系统申请空间时都需要一定的时间,如果数据过大但一个一个的去申请,则会大大增加运行时间。而为什么不给一个更大的值呢?是因为如果你给一个更大的值,但实际的需求没有那么多,就会造成空间上的浪费,虽然capaticy*2或许会造成空间上一定的浪费,但可以把量控制在一个合适的值中,既不会太大也不会太小。

    SLDateType* temp = (SLDateType*)realloc(ps->a, sizeof(SLDateType) * newcapaticy);      这段代码通过realloc来进行扩容,这里需要注意realloc中的第二个参数是sizeof(SLDateType) * newcapaticy,而不是只是newcapaticy。因为realloc的第二个参数为字节数,如果只传newcapaticy的话,假设newcapaticy=4,可以设想一下如果有8个LDateType类型的数据需要存储,也就是需要32个字节的大小,而realloc只给我扩容了4个字节的大小,那么肯定会造成溢出程序会崩掉。

        头部插⼊删除 / 尾部插⼊删除:

                头部插入:

        

        函数SeqListPushFront将实现顺序表的头插,参数SeqList* ps为原顺序表, SLDateType x为将要插入的数据。

        第一步:写入我们事先写好的扩容函数,判断是否需要进行扩容,

        第二步:因为我们是要插入数据,所以我们需要将源数据整体向后移动,而ps->size为有效数据位置的下一个位置。创建for循环,将循环遍历i设定为ps->size,这里需要理解为什么i不是为0而是ps->size。

                从后往前插入:

                

        可以看到当i=ps->size,循环退出条件为i>0时候就能将整个数组进行向后移动一位,最后在a[0]的位置将数据给成x值。

                从前往后插入:

       

        从前向后插入的话,可以看到a[0]后的数据完全被a[0]给覆盖了,导致了数据丢失。

所以顺序表在选择头插的话会使用从后向前的方法,而不是从前向后的方法。

                头部删除:

        

        将顺序表进行头部删除的时,应先判断顺序表里是否有数值,删除需要在顺序表里有值的时候才进行删除,所以这里使用assert(断言)进行断言操作。

        接着通过for循环,这里将循环遍历i赋值为0,属于从前往后覆盖而不是从后往前覆盖,具体为什么参考上面从前往后插入的例子。

        

        当i<ps->size-1(倒数第二个数据)的位置时候就结束循环,最后只需要将size--,就完成了顺序表的头删,可能这里有些小疑惑,那我删除了后需不需要将容量缩小,我想说不需要,容量值不需要改变。

                 尾部插入:

        

        尾部插入就很简单了,因为size是有效数据位置的的下一个位置,只需要在size位置去赋值,然后将size++就完成了,但插入前依然得先检查一下容量是否足够。

                尾部删除:

        

        尾部删除也很简单,只需要先判断一下顺序表里有没有数据,接着直接将size--就好了,那可能有些小伙伴就有疑问,size--前需不需要先改变一下ps->a[ps->size]里的数值?其实不需要,因为你后面要是插入的话是不影响的。原先的值会被改成插入的值。

         指定位置之前插⼊/删除数据: 

                指定位置前插入:

        指定位置前插入其实跟头插没什么区别,只是将头部的位置变成需要传进来的位置。而在这之前需要我们先去断言一下传进来的位置是否合理就行。

                指定位置前删除:

        指定位置的删除除了判断需要删除的位置是否合理,还得多增加一个判断size是否为0,用来判断顺序表里是否还要数据,其他跟头删是一样的。

        

 

            

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值