mirror of
https://github.com/happyflyer/wangdao-data-structure.git
synced 2026-04-25 11:11:34 +08:00
完成顺序表
This commit is contained in:
43
README.md
43
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)
|
||||
|
||||
65
ch2/README.md
Normal file
65
ch2/README.md
Normal 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. 静态链表
|
||||
@@ -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)$
|
||||
@@ -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
138
ch2/sequence/dynamic.cpp
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user