[数据结构]线性表中简单的顺序表
线性表(Linear List)
线性表是具有相同数据类型
,每个数据元素所占空间一样大
的n个数据元素的有限
序列(有次序)。
L=($a_1,a_2,…,a_i,a_{i+1},…,a_n$)
其中$a_i$是线性表中的“第i个”元素线性表中的位序
$a_1$是表头元素;$a_n$是表尾元素。
除第一个元素外,每个元素有且仅有一个直接前驱
;除最后一个元素外,每个元素有且仅有一个直接后继
。
0x01 线性表的常用实现函数:
InitList(&L): 初始化表。构造一个空的线性表L,分配内存空间。
DestroyList(&L):销毁操作。销毁线性表,并释放线性表L所占用的内存空间。
ListInsert(&L,i,e):插入操作。在表L中的第i个位置上插入指定元素e。
ListDelete(&L,i,&e):删除操作。删除表L中第i个位置的元素,并用e返回删除元素的值。
LocateElem(L,e):按值查找操作。在表L中查找具有给定关键字值的元素。
GetElem(L,i):按位查找操作。获取表L中第i个位置的元素的值。
其他常用操作: Length(L):求表长。返回线性表L的长度,即L中数据元素的个数。
PrintList(L):输出操作。按前后顺序输出线性表L的所有元素值。
Empty(L):判空操作。若L为空表,则返回true,否则返回false。
0x02 顺序表
顺序表是用顺序存储
的方式实现线性表。
顺序存储:
把逻辑上相邻的元素存储在物理位置上也相邻的存储单元中,元素之间的关系由存储单元的邻接关系来体现。
顺序表的特点:
- 随机访问,即可在O(1)的时间内找到第 i 个元素。
- 存储密度高,每个节点之存储数据元素。
- 扩展容量不方便(即便采用动态分配的方式实现,拓展长度的时间复杂度也比较高)。
- 插入、删除操作不方便,需要移动大量元素。
0x0201 顺序表–静态分配
静态分配方式:
1 |
|
举个栗子:
1 |
|
顺序表的静态分配的局限性在于顺序表的表长在开始时就要确定,然后就无法更改(存储空间是
静态
的)。
0x0202 顺序表–动态分配
为了动态分配存储空间,提高内存空间的使用效率,可以采用动态分配的分配方式。
动态分配方式:
1 |
|
动态分配:动态申请和释放内存空间。
- 申请:malloc函数
- 释放:free函数
举个栗子:
1 |
|
0x0203 顺序表的插入
实现:
举第一个栗子🌰:
1 | /*** |
此处省略:
- 判断i是否合法,即
i
在[1,length+1]
。 - 判断顺序表是否存满,若存满则无法插入数据。
- 返回是否操作成功的提示。
举第二个栗子🌰(改进后):
1 | /*** |
时间复杂度分析:
最好情况:新元素插入到表尾,不需要移动元素。
i = n + 1,循环0次;最好时间复杂度 = $O(1)$
最坏情况:新元素插入到表头,需要将n个元素全部向后移动
i = 1,循环n次;最坏时间复杂度 = $O(n)$
平均情况:新元素插入到任何一个位置的概率相同,即 i = 1,2,3,……,length+1 的概率都是$p = 1/(n+1)$
当i = 1,循环n次;当i = 2时,循环n - 1次;当i = 3时,循环n - 2次;……当i = n + 1时,循环0次。
平均循环次数 = $np+(n-1)p+(n-2)p+……+1·p = [\frac{n(n+1)}2]·[\frac{1}{n+1}]=\frac{n}2$
$\therefore$ 平均时间复杂度 = $O(n)$
0x0204 顺序表的删除
实现:
举个栗子🌰:
1 | /*** |
时间复杂度分析:
删除操作的时间复杂度和插入操作的时间复杂度相同。
0x0205 顺序表的查找(按位查找)
实现:
1 | /*** |
时间复杂度分析:
直接返回值,时间复杂度 = $O\left(1\right)$
0x0206 顺序表的查找(按值查找)
实现:
1 | /*** |
ps:C语言中不能用
==
来直接比较两个结构体变量。
时间复杂度分析:
最好情况:目标元素在表头。
循环1次;最好时间复杂度 = $O(1)$
最坏情况:目标元素在表尾
循环n次;最坏时间复杂度 = $O(n)$
平均情况:假设目标元素出现在任何一个位置的概率相同,概率都是$p = \cfrac 1n$
平均循环次数 = $1\cdot\cfrac1n+2\cdot\cfrac1n+3\cdot\cfrac1n+……+n\cdot\cfrac1n = [\frac{n(n+1)}2]·[\frac{1}{n}]=\frac{n+1}2$
$\therefore$ 平均时间复杂度 = $O(n)$