---
title: 线性表
---
## 基础概念和基本操作
> 强调线性表是一种逻辑结构,不是存储结构
### 定义
线性表是具有相同数据类型的n(n≥0)个数据元素的有限序列。一般表示:
L=(a1,a2,a3......an) 其中n可以理解为表长(线性表的长度),n=0时候,即表空
- `表头元素`:线性表中唯一的“第一个”数据元素,例如a1
- `表尾元素`:线性表中唯一的“最后一个”数据元素,例如an
重要逻辑特性:
- 除表头元素外,线性表中每个元素有且仅有一个`直接前驱`
- 除表尾元素外,线性表中每个元素有且仅有一个`直接后继`
基于此,这种**线性有序的逻辑结构**,使得线性表的特点如下:
- 元素的**个数有限**(强调有限序列)
- 元素在逻辑上具有**顺序性**,在序列中每个元素都是都有先后次序的
- 元素都数据元素,**每个元素都是单个元素**
- 元素的**数据类型都相同**(强调相同数据类型),每个数据元素占用相同大小的存储空间
- 元素具有**抽象性**,仅仅讨论元素之间的逻辑关系,不需要去考虑元素究竟表示的什么内容
> Tips: **线性表是一种逻辑结构**,表示元素之间一对一的相邻关系。**顺序表和链表则指的是存储结构**
### 基本操作
- `InitList(&L)`: **初始化表**。构造空的线性表
- `Length(L)`:**获取表的长度**。返回线性表L的长度,即表中的数据元素个数
- `LocateElem(L,e)`:**按值查找操作**。在表L中国查找具有给定关键字的元素
- `GetElem(L,i)`:**按位查找操作**。获取表中第i个位置的元素的值
- `ListInsert(&L,i,e)`:**插入操作**。在表的第i个位置上插入指定元素e
- `ListDelete(&L,i,&e)`:**删除操作**。删除表中第i个位置的元素,并用e返回删除元素的值
- `PrintList(L)`:**输出操作**。按照前后顺序(如:1、2....n)输出线性表的所有元素值
- `Empty(L)`:**判空操作**。当表L为空,则返回true,否则返回false
- `DestoryList(&L)`:**销毁操作**。将线性表销毁,释放线性表L所占用的内存空间(类似:释放内存)
线性表是具有相同的数据类型的有限个数据元素组成的,**数据元素是由数据项组成的**
## 线性表的顺序表示
### 定义
`顺序表`:顺序存储的线性表,**是用一组地址连续的存储单元,依次存储线性表中的数据元素,使得在逻辑上相邻的两个元素在物理位置上也相邻。**
顺序表中的元素的逻辑顺序与实际的物理位置相同
注意:
- 线性表中的元素的位序是从1开始的,例如1、2、3...
- 数组中的元素的下标是从0开始的,例如0、1、2...
```c
# define MaxSize 20 // 定义常量MaxSize 用来声明顺序表的最大长度
// 线性表结构体定义【ElemType用来代指顺序表中元素的类型,例如高级语言中的int、string....】
typedef struct{
ElemType data[MaxSize]; // 顺序表的元素
int length; // 顺序表的长度
}SqList
```
#### 存储分配
`静态分配`:数组的大小和空间都是实现确定好的,一旦存储空间占满就会产生溢出,直接导致程序崩溃。(有点内存不够,宕机重启的意思....)
`动态分配`:存储数据的空间在程序执行过程中通过`动态存储分配语句`分配的,即便是数据空间占满,也可以另外开辟一块更大的空间,来替换原来的存储空间,满足扩充数据空间的目的。(有点动态规划的意思....)最重要的是:**不需要像静态分配那样,一次性地固定线性表的空间和大小**
```c
#define InitSize 100 // 表长度初始化
// 动态分配数组顺序表的结构体定义
typedef struct{
ElemType *data; // 动态分配数组的指针
int MaxSize,length; // 数组的最大容量和当前元素个数
}SqList;
```
动态分配语句
```cpp
// C语言中
L.data=(ElemType*)malloc(sizeof(ElemType)*InitSize);
// C++ 中
L.data=new ElemType[InitSize];
```
`malloc()函数`: 指针型函数,返回的指针指向该分配域的开头的位置。作用是在内存的动态存储区中分配一个长度为size的连续空间。[百度百科](https://baike.baidu.com/item/malloc%E5%87%BD%E6%95%B0/8582146?fr=aladdin)
**动态分配不是链式存储,而是属于顺序存储结构**,动态分配的物理结构没有改变,依然是随机存取的方式。只是分配的空间大小可以在运行时决定;
#### 顺序表的特点
- 随机访问【这是最主要的特点】,通过存储起始地址和元素序号O(1)时间内访问指定元素。
- 存储密度高,没有结点只存储数据元素,不像索引存储那样,还需要索引表什么的..
- 逻辑上相邻的元素物理上也相邻,插入和删除需要移动大量元素
### 基本操作
#### 插入
在顺序表L的第i(1≤i≤L.length+1)个位置插入新的元素e
- 第一步:如果i非法,则直接返回false,插入失败,结束插入过程
- 第二步:i正常,将表的第i个元素以及后面的所有元素都像有移动一个位置,在腾出来的空位置插入元素e
- 第三步:顺序表插入成功,返回true
注意:先判空和临界值,提高算法健壮性
```cpp
/*
* @Description: 顺序表的插入操作
* @Version: Beta1.0
* @Author: 【B站&公众号】Rong姐姐好可爱
* @Date: 2020-02-23 07:48:26
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
* @LastEditTime: 2020-02-23 07:48:26
*/
bool ListInsert(SqList &L, int i, ElemType e){
// i非法 i=1 表头 i=L.length+1 表尾巴
if(i<1||i>L.length+1){
return false;
}
// 存储空间满,无法插入
if(L.length >= MaxSize){
return false;
}
// 遍历,将位置元素往后移动,注意从后往前循环,避免值被覆盖
for(int j=L.length; j>=i;j--){
L.data[j]=L.data[j-1];
}
// 此时,表L中的第i个元素和第i+1元素素值一样,将新元素存入i位置即可
// 第i个元素,对应的位置角标为i-1
L.data[i-1]=e;
// 表长度加1
L.length++;
// 返回插入成功
return true;
}
```
注意:区别顺序表中的位序和角标;
**时间复杂度**
- 最好情况:在表尾插入,元素向后移动循环没有执行,时间复杂度O(1);
- 最坏情况:在表头插入,元素后移循环执行n次,时间复杂度为O(n);
- 平均情况:随机插入,平均次数为:n/2,对应的平均复杂度为O(n);
**线性表插入算法的平均时间复杂度为:O(n)**
> Tips: 需要根据实现代码理解循环为什么是从后往前来实现元素后移,通过for循环可以很明显的看出表尾插入快,表头插入慢
#### 删除
删除顺序表L中第i(1≤i≤L.length+1)个位置的元素
- 成功,返回true,将被删除的元素用引用变量返回;
- 失败,返回false
```C++
/*
* @Description: 顺序表的删除操作
* @Version: Beta1.0
* @Author: 【B站&公众号】Rong姐姐好可爱
* @Date: 2020-02-23 07:48:26
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
* @LastEditTime: 2020-02-23 07:48:26
*/
bool ListDelete(SqList &L, int i, ElemType &e){
// i非法 i=1 表头 i=L.length+1 表尾巴
if(i<1||i>L.length+1){
return false;
}
// 存储空间满,无法插入
if(L.length >= MaxSize){
return false;
}
// 引用变量e赋值
e=L.data[i-1]
// 遍历,第i个元素后面的往前移动
for(int j=i; j<=L.length;j++){
// 从第i个元素开始,角标从i-1开始
L.data[j-1]=L.data[j];
}
// 此时,表L中的表尾元素和倒数第二个元素值一样,将表的长度-1
// 表长度减1
L.length--;
// 返回删除成功
return true;
}
```
从这里来看,删除、插入元素都会涉及到大量的元素的移动(最好情况例外),总结而言:
- 元素从后往前移,循环从前往后遍历
- 元素从前往后移,循环从后往前遍历
**时间复杂度:**
- 最好情况:删除表尾元素,不需要移动任何元素,时间复杂度为O(1);
- 最坏情况:删除表头元素,需要移动除第一个元素外的所有元素,时间复杂度为O(n);
- 平均情况:随机删除,平均需要(n-1)/2,对应的时间复杂度为O(n);
**线性表删除算法的平均时间复杂度为O(n);**
#### 按值查找(顺序查找)
在顺序表L中查找第一个元素值等于e的元素,并返回位序
```cpp
/*
* @Description: 顺序表的按值查找(顺序查找)
* @Version: Beta1.0
* @Author: 【B站&公众号】Rong姐姐好可爱
* @Date: 2020-02-23 07:48:26
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
* @LastEditTime: 2020-02-23 07:48:26
*/
int LocateElem(SqList L,ElemType e){
int i;
// 循环判断
for(i=0;i