目录
动态顺序表的概念及结构
概念
我们知道一维数组可以是静态分配的,也可以是动态分配的。而静态顺序表只适用于知道需要存多少数据的场景,静态顺序表的MAXSIZE一旦给定就不能更改了,空间开多了浪费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间大小,所以动态顺序表使用动态开辟的数组进行存储。
对于动态顺序表,在动态分配时,存储数组的空间是在程序执行过程中通过动态存储分配语句分配的,一旦数据空间占满,就将原有的存储空间扩充或者另外开辟一块更大的存储空间,用以替换原来的存储空间,从而达到扩充存储数组空间的目的,而不需要为线性表一次性地划分所有空间。
结构
动态顺序表的结构是在静态顺序表的结构上加入一个记录当前动态数组最大容量的变量。
动态顺序表的接口实现
初始化SeqListInit
动态顺序表的初始化,只需要将指向动态数组的指针初始化为NULL,size与capacity初始化为0就可以了。
销毁SeqListDestroy
动态顺序表的销毁,就是将动态开辟的空间进行释放,避免内存泄漏。
打印动态顺序表SeqListPrint
打印动态顺序表就是将动态数组遍历一次。
尾插SeqListPushBack
尾插的思路很简单,只需要执行ps->a[ps->size]=x语句;因为动态数组的下标是从0开始的,而size表示当前有效数据的个数,所以有效数据的区间为[ 0 ,size-1 ],ps->a[ps->size]刚好表示最后一个元素的下一个空间位置。
但在添加数据时,我们需要检查容量,如果容量不够我们需要进行增容。
需要注意的是:
1.当NULL作为参数传给realloc时,作用与malloc一样在堆上开辟新空间。
2.扩容的时候有两种情况,一种是原地扩容,另一种是异地扩容。
头插SeqListPushFront
进行头插操作,需要将所有数据往后移动一个位置,将下标为0的空间空出来,用来存储插入的数据。
尾删SeqListPopBack
在进行尾删的操作之前,我们需要进行判断动态顺序表是否为空,如果为空说明没有数据不需要删除,如果不为空可以进行尾删的操作。
头删SeqListPopFront
头删的操作就是将除下标为0的数据往前移动一个位置,将头部的数据进行覆盖。
插入SeqListInsert
插入的思想与头插的思想相似,只不过从pos位置开始移动。这里的pos是指下标。
删除SeqListErase
删除的操作也是要进行数据的覆盖,将pos位置后面的数据向前移动一个空间位置,覆盖掉原来pos位置的数据。
数据覆盖的写法有两种,这两种写法的思想都是一样的,取决于你更理解哪一个。
// 1. int begin = pos+1; while (begin < ps->size) { ps->a[begin-1] = ps->a[begin]; begin++; } // 2. int begin = pos; while (begin <ps->size-1 ) { ps->a[pos] = ps->a[pos + 1]; begin++; }
查找SeqListFind
因为动态顺序表的物理结构是连续的,所以可以使用循环进行下标遍历。如果找到了,返回数据存储空间的下标,如果没有找到返回-1.
修改SeqListModify
修改对应下标的元素,在修改之前需要进行pos下标的合法性检查。
动态顺序表的优缺点
优点
1.因为顺序表的物理空间是连续的,所以顺序表最主要的特点是随机访问,即通过首地址 和下标可在时间O(1)内找到指定的元素。
2.分配的空间大小可以在运行时动态决定。
缺点
1.空间不够,需要进行扩容,但扩容有一定的性能消耗。
2.扩容一般都是成倍数的扩,存在一定的空间浪费。
3.因为顺序表逻辑上相邻的元素物理上也相邻,所以头部或者中间位置上的插入与删除需要挪动大量的元素,效率低下。