完成顺序表

This commit is contained in:
lifei
2020-11-29 13:50:44 +08:00
parent c81f767014
commit 925f66470c
7 changed files with 335 additions and 121 deletions

View File

@@ -2,23 +2,24 @@
## 1. 基本概念
- 基本概念
- 数据
- 数据元素、数据
- 数据对象、数据结构
- 数据类型、抽象数据类型ADT
- 三要素
- **逻辑结构**
- 集合
- 线性结构
- 树形结构
- 图状结构(网状结构
- **物理结构**(存储结构)
- 顺序存储
- 链式存储
- 索引存储
- 散列存储
- **数据的运算**
- 数据
- 数据元素、数据项
- 数据对象、数据结构
- 数据类型、抽象数据类型ADT
### 1.1. 三要素
- **逻辑结构**
- 集合
- 线性结构
- 树形结构
- 图状结构(网状结构)
- **物理结构**(存储结构)
- 顺序存储
- 链式存储
- 索引存储
- 散列存储
- **数据的运算**
逻辑结构:
@@ -41,6 +42,8 @@
- 运算的定义是针对逻辑结构的。
- 运算的实现是针对存储结构的。
### 1.2. 数据类型
数据类型是一个值的集合和定义在此几何上的一组操作的总称。
- 原子类型。其值不可再分的数据类型。
@@ -53,7 +56,7 @@
- 逻辑结构
- 数据的运算
在讨论一种数据结构时
### 1.3. 在讨论一种数据结构时
1. 定义逻辑结构,数据元素之间的关系
2. 定义数据的运算,针对现实需求,应该对这种逻辑结构进行什么样的运算
@@ -64,7 +67,7 @@
程序 = 数据结构 + 算法
- 数据结构:如何把现实世界的问题信息化,将信息存进计算机。同时还要实现对数据结构的基本操作。
- 算法:如何处理这些信息,以解决解决实际问题。
- 算法:如何处理这些信息,以解决实际问题。
算法的特性:
@@ -244,3 +247,5 @@ O(n!)
\lt
O(n^n)
$$
## 3. [线性表](ch2/README.md)

65
ch2/README.md Normal file
View File

@@ -0,0 +1,65 @@
# 线性表
## 1. 定义
线性表Linear List是具有相同数据类型的 $n$ $(n \geq 0)$ 个数据元素的有序序列。
- **相同数据类型**
- **有序**
- **序列**
其中 $n$ 为**表长**。当 $n=0$ 时线性表是一个**空表**。
$$
L=(\alpha_1,\alpha_2,...,\alpha_i,\alpha_{i+1},...,\alpha_n)
$$
- $\alpha_i$ 是线性表中的 “第 $i$ 个” 元素线性表中的**位序**。
- $\alpha_1$ 是**表头元素**$\alpha_n$ 是**表尾元素**。
- 出第一个元素外,每个元素有且仅有一个**直接前驱**;除最后一个元素外,每个元素有且仅有一个**直接后继**。
> 注意:位序是从 $1$ 开始的,而数组下标是从 $0$ 开始的。
## 2. 基本操作
- `InitList(&L)`:初始化表。构造一个空的线性表 $L$,分配内存空间。
- `DestroyList(&L)`:销毁操作。销毁线性表,并释放线性表 $L$ 所占用的内存。
- `ListInsert(&L,i,e)`:插入操作。在表 $L$ 中的第 $i$ 个位置插入指定元素 $e$。
- `ListDelete(&L,i,&e)`:插入操作。删除表 $L$ 中的第 $i$ 个位置的元素,并用 $e$ 返回删除元素的值。
- `GetElem(L,i)`:按位查找操作。获取表 $L$ 中的第 $i$ 个位置的元素的值。
- `LocateElem(L,e)`:按值查找操作。在表 $L$ 中查找查找具有给行关键字值的元素。
- `Length(L)`:求表长。返回线性表 $L$ 的长度,即 $L$ 中所有元素的个数。
- `PrintList(L)`:输出操作。按前后顺粗输出线性表 $L$ 的所有元素值。
- `Empty(L)`:判空操作。若 $L$ 为空表,则返回 `true`,否则返回 `false`
Tips
- 对数据的操作(记忆思路):创销、增删改查。
- C 语言函数的定义:<返回值类型> 函数名(<参数 1 类型> 参数 1, <参数 2 类型> 参数 2, ...)。
- 实际开发中,可根据实际需求定义其他的基本操作。
- 函数名和参数的形式、命名都可改变(参考:严蔚敏《数据结构》)。(可读性)
- 什么时候要传入引用 `&`:对参数的修改结果需要 “带回来”。C++ 语法)
为什么要实现对数据结构的基本操作:
- 团队合作编程,你定义的数据结构要让别人能够很方便的使用(封装)。
- 将常用的操作、运算封装成函数,避免重复工作,降低出错风险。
## 3. 按照存储/物理结构分类
- 顺序表(顺序存储)
- 链表(链式存储)
- 单链表
- 双链表
- 循环链表
- 静态链表
## 4. [顺序表](sequence/README.md)
## 5. 单链表
## 6. 双链表
## 7. 循环链表
## 8. 静态链表

View File

@@ -1,56 +1,4 @@
# 线性
## 1. 定义
线性表Linear List是具有相同数据类型的 $n$ $(n \geq 0)$ 个数据元素的有序序列。
- **相同数据类型**
- **有序**
- **序列**
其中 $n$ 为**表长**。当 $n=0$ 时线性表是一个**空表**。
$$
L=(\alpha_1,\alpha_2,...,\alpha_i,\alpha_{i+1},...,\alpha_n)
$$
- $\alpha_i$ 是线性表中的 “第 $i$ 个” 元素线性表中的**位序**。
- $\alpha_1$ 是**表头元素**$\alpha_n$ 是**表尾元素**。
- 出第一个元素外,每个元素有且仅有一个**直接前驱**;除最后一个元素外,每个元素有且仅有一个**直接后继**。
> 注意:位序是从 $1$ 开始的,而数组下标是从 $0$ 开始的。
## 2. 基本操作
- `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`
Tips
- 对数据的操作(记忆思路):创销、增删改查。
- C 语言函数的定义:<返回值类型> 函数名(<参数 1 类型> 参数 1, <参数 2 类型> 参数 2, ...)。
- 实际开发中,可根据实际需求定义其他的基本操作。
- 函数名和参数的形式、命名都可改变(参考:严蔚敏《数据结构》)。(可读性)
- 什么时候要传入引用 `&`:对参数的修改结果需要 “带回来”。C++ 语法)
为什么要实现对数据结构的基本操作:
- 团队合作编程,你定义的数据结构要让别人能够很方便的使用(封装)。
- 将常用的操作、运算封装成函数,避免重复工作,降低出错风险。
存储/物理结构:
- 顺序表(顺序存储)
- 链表(链式存储)
## 3. 顺序表
# 顺序
顺序表:用顺序存储方式实现的线性表。
@@ -61,9 +9,9 @@ Tips
- $LOC(L)+2*sizeof(ElemType)$
- $...$
### 3.1. 静态分配
## 1. 静态分配
```c
```cpp
#define MaxSize 10
typedef struct
{
@@ -72,14 +20,25 @@ typedef struct
} SqList;
```
```cpp
void InitList(SqList &L)
{
// for (int i = 0; i < MaxSize; i++)
// {
// L.data[i] = 0;
// }
L.length = 0;
}
```
$MaxSize*sizeof(ElemType)$
- 使用“静态数组”实现
- 大小一旦确定就无法改变
### 3.2. 动态分配
## 2. 动态分配
```c
```cpp
#define InitSize 10
typedef struct
{
@@ -89,6 +48,30 @@ typedef struct
} 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`:释放内存空间
@@ -103,14 +86,14 @@ C++ 可以使用 `new``delete` 关键字。
- 顺序表存满时,可再用 `malloc` 动态扩展顺序表的最大容量
- 需要将数据元素复制到新的存储区域,并用 `free` 函数释放原区域
### 3.3. 顺序表的特点
## 3. 顺序表的特点
- **随机访问**:即可以在 $O(1)$ 时间内找到第 $i$ 个元素。
- 存储密度高:每个节点只存储数据元素。
- 扩展容量不方便:静态分配不能扩展容量,动态分配可以扩展但时间复杂度高。
- 插入、删除元素操作不方便,需要移动大量元素。
### 3.4. 插入操作
## 4. 插入操作
$$
L=(\alpha_1,\alpha_2,\alpha_3,\alpha_4,\alpha_5)
@@ -150,7 +133,7 @@ bool ListInsert(SqList &L, int i, int e)
- 最坏时间复杂度:$O(n)$
- 平均时间复杂度:$O(n)$
### 3.5. 删除操作
## 5. 删除操作
```cpp
bool ListDelete(SqList &L, int i, int &e)
@@ -184,6 +167,39 @@ bool ListDelete(SqList &L, int i, int &e)
- 移动元素时,从靠前的元素开始?还是从表尾元素开始?
- 分析代码,理解为什么有的参数需要加 `&`C++ 语法中的引用)。
### 3.6. 按位查找
## 6. 按位查找
### 3.7. 按值查找
```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)$

View File

@@ -1,39 +0,0 @@
#include <stdlib.h>
#define InitSize 10
typedef struct
{
int *data;
int MaxSize;
int length;
} SeqList;
// 初始化顺序表,动态分配
void InitList(SeqList &L)
{
// 用 malloc 函数申请一篇连续的存储空间
L.data = (int *)malloc(InitSize * sizeof(int));
L.length = 0;
L.MaxSize = InitSize;
}
// 增加动态数组的长度
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)
}
int main()
{
SeqList L;
InitList(L);
// 往顺序表里面插入元素
IncreaseSize(L, 5);
return 0;
}

138
ch2/sequence/dynamic.cpp Normal file
View File

@@ -0,0 +1,138 @@
#include <stdio.h>
#include <stdlib.h>
#define InitSize 10
typedef struct
{
int *data;
int MaxSize;
int length;
} SeqList;
// 初始化顺序表,动态分配
void InitList(SeqList &L)
{
// 用 malloc 函数申请一篇连续的存储空间
L.data = (int *)malloc(InitSize * sizeof(int));
L.length = 0;
L.MaxSize = InitSize;
}
// 增加动态数组的长度
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);
}
// 插入元素操作,要将元素 e 放到数组 L 下标为 i-1 的位置
bool ListInsert(SeqList &L, int i, int e)
{
// i 的值必须是合法的位序
if (i < 1 || i > L.length + 1)
{
return false;
}
// 如果存储空间已满,则不能插入
if (L.length >= L.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;
}
// 删除元素操作
bool ListDelete(SeqList &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;
}
// 按位查找
int GetElem(SeqList L, int i)
{
// i 的值必须是合法的位序
if (i < 1 || i > L.length)
{
return false;
}
return L.data[i - 1];
}
// 按值查找
int LocateElem(SeqList L, int e)
{
for (int i = 0; i < L.length; i++)
{
if (L.data[i] == e)
{
return i + 1;
}
}
return 0;
}
int main()
{
SeqList L;
InitList(L);
// 往顺序表里面插入元素
ListInsert(L, 1, 1);
ListInsert(L, 2, 2);
ListInsert(L, 3, 3);
ListInsert(L, 4, 4);
ListInsert(L, 5, 5);
ListInsert(L, 3, 100);
// 扩充动态数组长度
printf("动态数组最大容量为=%d\n", L.MaxSize);
IncreaseSize(L, 5);
printf("动态数组最大容量为=%d\n", L.MaxSize);
// 打印操作
for (int i = 0; i < L.length; i++)
{
printf("data[%d]=%d\n", i, L.data[i]);
}
// 测试删除操作
int e = -1;
if (ListDelete(L, 5, e))
{
printf("已删除的元素值为=%d\n", e);
}
else
{
printf("位序不合法,删除失败\n");
}
// 打印操作
for (int i = 0; i < L.length; i++)
{
printf("data[%d]=%d\n", i, L.data[i]);
}
// 测试按位查找
printf("位序为4的元素值为=%d\n", GetElem(L, 4));
// 测试按值查找
printf("元素值为4的元素位序为=%d\n", LocateElem(L, 4));
return 0;
}

View File

@@ -39,6 +39,7 @@ bool ListInsert(SqList &L, int i, int e)
return true;
}
// 删除元素操作
bool ListDelete(SqList &L, int i, int &e)
{
// i 的值必须是合法的位序
@@ -56,6 +57,30 @@ bool ListDelete(SqList &L, int i, int &e)
return true;
}
// 按位查找
int GetElem(SqList L, int i)
{
// i 的值必须是合法的位序
if (i < 1 || i > L.length)
{
return false;
}
return L.data[i - 1];
}
// 按值查找
int LocateElem(SqList L, int e)
{
for (int i = 0; i < L.length; i++)
{
if (L.data[i] == e)
{
return i + 1;
}
}
return 0;
}
int main()
{
SqList L;
@@ -73,7 +98,7 @@ int main()
}
// 测试删除操作
int e = -1;
if (ListDelete(L, 7, e))
if (ListDelete(L, 5, e))
{
printf("已删除的元素值为=%d\n", e);
}
@@ -86,5 +111,9 @@ int main()
{
printf("data[%d]=%d\n", i, L.data[i]);
}
// 测试按位查找
printf("位序为4的元素值为=%d\n", GetElem(L, 4));
// 测试按值查找
printf("元素值为4的元素位序为=%d\n", LocateElem(L, 4));
return 0;
}