diff --git a/README.md b/README.md index cfb2e1c..9f91036 100644 --- a/README.md +++ b/README.md @@ -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) diff --git a/ch2/README.md b/ch2/README.md new file mode 100644 index 0000000..89bd7da --- /dev/null +++ b/ch2/README.md @@ -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. 静态链表 diff --git a/ch2.md b/ch2/sequence/README.md similarity index 51% rename from ch2.md rename to ch2/sequence/README.md index 5a89ba5..f4723a0 100644 --- a/ch2.md +++ b/ch2/sequence/README.md @@ -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)$ diff --git a/ch2/sequence/dynamic.c b/ch2/sequence/dynamic.c deleted file mode 100644 index 8fb4600..0000000 --- a/ch2/sequence/dynamic.c +++ /dev/null @@ -1,39 +0,0 @@ -#include -#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; -} diff --git a/ch2/sequence/dynamic.cpp b/ch2/sequence/dynamic.cpp new file mode 100644 index 0000000..83a748d --- /dev/null +++ b/ch2/sequence/dynamic.cpp @@ -0,0 +1,138 @@ +#include +#include +#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; +} diff --git a/ch2/sequence/static.c b/ch2/sequence/static.cpp similarity index 73% rename from ch2/sequence/static.c rename to ch2/sequence/static.cpp index 0abd75c..c398a6a 100644 --- a/ch2/sequence/static.c +++ b/ch2/sequence/static.cpp @@ -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; } diff --git a/ch2/tips2.c b/ch2/tips2.cpp similarity index 100% rename from ch2/tips2.c rename to ch2/tips2.cpp