Files
2020-11-29 19:47:37 +08:00

206 lines
4.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 顺序表
顺序表:用顺序存储方式实现的线性表。
顺序存储:把逻辑上相邻的元素存储在物理位置上也相邻的存储单元中,元素之间的关系由存储单元的邻接关系来体现。
- $LOC(L)$
- $LOC(L)+sizeof(ElemType)$
- $LOC(L)+2*sizeof(ElemType)$
- $...$
## 1. 静态分配
```cpp
#define MaxSize 10
typedef struct
{
ElemType data[MaxSize];
int length;
} SqList;
```
```cpp
void InitList(SqList &L)
{
// for (int i = 0; i < MaxSize; i++)
// {
// L.data[i] = 0;
// }
L.length = 0;
}
```
$MaxSize*sizeof(ElemType)$
- 使用“静态数组”实现
- 大小一旦确定就无法改变
## 2. 动态分配
```cpp
#define InitSize 10
typedef struct
{
ElemType *data;
int MaxSize;
int length;
} SeqList;
```
```cpp
void InitList(SeqList &L)
{
// 用 malloc 函数申请一篇连续的存储空间
L.data = (int *)malloc(InitSize * sizeof(int));
L.length = 0;
L.MaxSize = InitSize;
}
```
```cpp
void IncreaseSize(SeqList &L, int len)
{
int *p = L.data;
L.data = (int *)malloc((L.MaxSize + len) * sizeof(int));
for (int i = 0; i < L.length; i++)
{
L.data[i] = p[i]; // 时间复杂度高
}
L.MaxSize += len;
free(p);
}
```
- `malloc`:动态申请一整片连续的内存空间
- `free`:释放内存空间
```c
L.data = (ElemType *) malloc(sizeof(ElemType) * InitSize)
```
C++ 可以使用 `new``delete` 关键字。
- 使用“动态数组”实现
- `L.data = (ElemType *) malloc(sizeof(ElemType) * InitSize)`
- 顺序表存满时,可再用 `malloc` 动态扩展顺序表的最大容量
- 需要将数据元素复制到新的存储区域,并用 `free` 函数释放原区域
## 3. 顺序表的特点
- **随机访问**:即可以在 $O(1)$ 时间内找到第 $i$ 个元素。
- 存储密度高:每个结点只存储数据元素。
- 扩展容量不方便:静态分配不能扩展容量,动态分配可以扩展但时间复杂度高。
- 插入、删除元素操作不方便,需要移动大量元素。
## 4. 插入操作
$$
L=(\alpha_1,\alpha_2,\alpha_3,\alpha_4,\alpha_5)
$$
$$
[a_0,a_1,a_2,a_3,a_4]
$$
```cpp
bool ListInsert(SqList &L, int i, int e)
{
// i 的值必须是合法的位序
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.data[i - 1] = e;
L.length++;
return true;
}
```
插入位置之后的元素都要后移。
- 最好时间复杂度:$O(1)$
- 最坏时间复杂度:$O(n)$
- 平均时间复杂度:$O(n)$
## 5. 删除操作
```cpp
bool ListDelete(SqList &L, int i, int &e)
{
// i 的值必须是合法的位序
if (i < 1 || i > L.length)
{
return false;
}
// 删除操作
e = L.data[i - 1];
for (int j = i; j < L.length; j++)
{
L.data[j - 1] = L.data[j];
}
L.length--;
return true;
}
```
删除位置之后的元素都要前移。
- 最好时间复杂度:$O(1)$
- 最坏时间复杂度:$O(n)$
- 平均时间复杂度:$O(n)$
代码要点:
- 代码中注意位序 $i$ 与数组下标的区别。
- 算法要有健壮性,注意判断 $i$ 的合法性。
- 移动元素时,从靠前的元素开始?还是从表尾元素开始?
- 分析代码,理解为什么有的参数需要加 `&`C++ 语法中的引用)。
## 6. 按位查找
```cpp
int GetElem(SqList L, int i)
{
// i 的值必须是合法的位序
if (i < 1 || i > L.length)
{
return false;
}
return L.data[i - 1];
}
```
- 时间复杂度:$O(1)$
## 7. 按值查找
```cpp
int LocateElem(SqList L, int e)
{
for (int i = 0; i < L.length; i++)
{
if (L.data[i] == e)
{
return i + 1;
}
}
return 0;
}
```
- 最好时间复杂度:$O(1)$
- 最坏时间复杂度:$O(n)$
- 平均时间复杂度:$O(n)$