【顺序表】
线性表的顺序存储又称顺序表,其使用一组地址空间连续的存储单元依次存储线性表中的数据元素,从而使得逻辑上相邻的两元素在物理存储空间上也相邻
顺序表具有以下特点:
1.支持随机访问:通过 data[i-1]
即可访问第 i
个元素,在 $O(1)$ 的时间复杂度内即可完成
2.存储密度高:只存储数据元素
3.扩展容量不方便:静态分配方式无法扩展,动态分配方式扩展要消耗大量的时间
4.插入、删除数据元素不方便
实现类如下:
1 |
|
【初始化表与销毁表】
在 C 语言中,连续的地址空间有两种分配方式:静态分配、动态分配
静态分配
静态分配事先规定好表长,表长声明后无法更改,之后根据表长的大小申请数组空间,一旦空间占满,再加入新的数据将会发生溢出
1 |
|
动态分配
动态分配时,顺序表的存储空间是通过动态存储分配语句分配的,当存储空间占满,可以另外开辟一块更大的存储空间,以替换原来的存储空间,从而达到扩充顺序表空间的目的
也就是说,在动态分配中,顺序表表长声明后可更改
在 C 语言中,使用 malloc()
函数与 free()
函数来进行空间的动态分配与回收;在 C++ 语言中,除了继承自 C 语言标准库的 malloc()
函数与 free()
函数,还引入了 new
操作符与 delete
操作符
相较于 malloc()
要手动计算类型大小,同时要强制转换成所需的返回类型(malloc()
默认返回类型为 void*
),new
操作符会自动计算类型大小,并返回对应类型指针
但从系统层面来说,真正开辟和回收空间的是 malloc()
函数与 free()
函数,new
操作符与 delete
操作符的实现依赖于内存管理接口
1 |
|
在动态分配顺序表空间时,若需要对顺序表进行扩充,则可先使用 malloc()
重新分配空间,然后再将原有数据复制到新空间,最后再将原空间释放即可
此外,也可使用 realloc()
重新分配空间
1 | template <typename T> |
【基本操作】
求表长
为避免使用顺序表 L
的表长时操作失误导致线性表表长更改,借助 OOP 中的 getter 思想,将获取顺序表表长封装成函数
1 | template <typename T> |
判空
当顺序表 L
表长为 $0$ 时,表为空表
1 | template <typename T> |
判满
当顺序表 L
表长为最大长度 maxLen
时,表为满表
1 | template <typename T> |
输出
对于顺序表 L
,按序输出其元素,首先为防止脏读的出现,应先判断表是否为空表,之后,从表头枚举到表尾即可
1 | template <typename T> |
关于脏读
在分配内存空间后,内存空间中可能存在数据,若不使用表长来制约循环,可能会出现脏读的情况
假设顺序表 L
表长为 $2$,其中仅有两个数据,分别为 $1$、$3$,且顺序表允许的最大表长为 $5$
当采用以下输出方式时,输出可能为:1,3,0,318,0
,其中 $318$ 即为原有内存空间的脏数据
1 | for (int i = 0; i < 4 ;i++) |
【插入】
在顺序表 L
上的 pos
位置插入新元素 elem
时,首先要进行越界判断与表满判断,防止溢出
之后将最后一个元素到原 pos
个元素间的所有元素后移一位,最后,将新元素 elem
插入到 pos
位置上,再将表长 len
加 $1$
需要注意的是,数组索引从 $0$ 开始,线性表索引从 $1$ 开始,因此 pos-1
为对应的数组位置
1 | template <typename T> |
时间复杂度分析
1.最好时间复杂度
最好的情况是在表尾 i=n+1
插入,此时后移语句不需要执行,时间复杂度为:$O(1)$
2.最坏时间复杂度
最坏的情况是在表头 i=1
插入,此时后移语句执行 $n$ 次,时间复杂度为:$O(n)$
3.平均时间复杂度
对于平均情况而言,在第 i
个位置插入概率为:
那么,在各位置上需要执行后移语句 $n-i+1$ 次,平均移动次数为:
此时,时间复杂度为:$O(n)$
【删除】
删除顺序表 L
上的 pos
位置的元素时,首先要进行越界判断与表空判断,防止溢出
之后将要删除的元素值赋给引用变量以最终返回元素值,最后,将要删除的元素 elem
到最后的元素间的所有元素前移一位,再将表长 len
减 $1$
1 | template <typename T> |
时间复杂度分析
1.最好时间复杂度
最好的情况是在表尾 i=n
删除,此时后移语句不执行,时间复杂度为:$O(1)$
2.最坏时间复杂度
最坏的情况是在表头 i=1
删除,此时后移语句执行 $n$ 次,时间复杂度为:$O(n)$
3.平均时间复杂度
对于平均情况而言,在第 i
个位置删除概率为:
那么,在各位置上需要执行后移语句 $n-i$ 次,平均移动次数为:
此时,时间复杂度为:$O(n)$
【查找】
按值查找
在顺序表 L
中寻找与给定元素值 elem
相同的第一个元素的位置时,需要进行表空判断,防止溢出
之后,从前向后对顺序表的元素进行枚举,若匹配成功,则直接返回对应位置
若对顺序表扫描完毕,没有匹配,说明匹配不成功,返回 -1
1 | template <typename T> |
时间复杂度分析
1.最好时间复杂度
最好的情况是在表头 i=1
查找,此时仅需比较 $1$ 次,时间复杂度为:$O(1)$
2.最坏时间复杂度
最好的情况是在表尾 i=n
查找,此时需要比较 $n$ 次,时间复杂度为:$O(n)$
3.平均时间复杂度
对于平均情况而言,查找元素在第 i
个位置的概率为:
那么,要在各位置上执行后移语句 $i$ 次,平均移动次数为:
此时,时间复杂度为:$O(n)$
按位查找
在顺序表 L
中查找第 pos
个位置的元素时,需要进行越界判断与表空判断,防止溢出
由于顺序表支持随机访问,因此直接读取 data[pos-1]
的值即可
1 | template <typename T> |
时间复杂度分析
由于顺序表支持随机访问,因此,时间复杂度为:$O(1)$