diff --git a/docs/manuscripts/ccp/cpp.sidebar.ts b/docs/manuscripts/ccp/cpp.sidebar.ts
index 2fa0fad..40404be 100644
--- a/docs/manuscripts/ccp/cpp.sidebar.ts
+++ b/docs/manuscripts/ccp/cpp.sidebar.ts
@@ -1,6 +1,32 @@
export const cppSidebar = [
{
- text: '计算机组成原理',
+ text: '引论',
+ prefix: 'basic-introduction',
+ children: []
+ },
+ {
+ text: '数据的表示和运算',
+ prefix: 'basic-introduction',
+ children: []
+ },
+ {
+ text: '存储系统',
+ prefix: 'basic-introduction',
+ children: []
+ },
+ {
+ text: '中央处理器',
+ prefix: 'basic-introduction',
+ children: []
+ },
+ {
+ text: '总线',
+ prefix: 'basic-introduction',
+ children: []
+ },
+ {
+ text: '输入、输出系统',
+ prefix: 'basic-introduction',
children: []
}
]
diff --git a/docs/manuscripts/ccp/readme.md b/docs/manuscripts/ccp/readme.md
index a9e8157..bea9ef3 100644
--- a/docs/manuscripts/ccp/readme.md
+++ b/docs/manuscripts/ccp/readme.md
@@ -1,4 +1,18 @@
# 计算机组成原理
-doing
\ No newline at end of file
+
+### 引论
+
+### 数据的表示和运算
+
+### 存储系统
+
+### 指令系统
+
+### 中央处理器
+
+### 总线
+
+### 输入/输出系统
+
diff --git a/docs/manuscripts/cn/cn.sidebar.ts b/docs/manuscripts/cn/cn.sidebar.ts
index ffa20b4..d538c96 100644
--- a/docs/manuscripts/cn/cn.sidebar.ts
+++ b/docs/manuscripts/cn/cn.sidebar.ts
@@ -1,6 +1,32 @@
export const cnSidebar = [
{
- text: '计算机网络',
+ text: '体系结构',
+ prefix: 'basic-introduction',
+ children: []
+ },
+ {
+ text: '物理层',
+ prefix: 'basic-introduction',
+ children: []
+ },
+ {
+ text: '数据链路层',
+ prefix: 'basic-introduction',
+ children: []
+ },
+ {
+ text: '网络层',
+ prefix: 'basic-introduction',
+ children: []
+ },
+ {
+ text: '传输层',
+ prefix: 'basic-introduction',
+ children: []
+ },
+ {
+ text: '应用层',
+ prefix: 'basic-introduction',
children: []
}
]
diff --git a/docs/manuscripts/cn/readme.md b/docs/manuscripts/cn/readme.md
index c6a45fe..11b1fe7 100644
--- a/docs/manuscripts/cn/readme.md
+++ b/docs/manuscripts/cn/readme.md
@@ -1,4 +1,10 @@
# 计算机网络
-doing
\ No newline at end of file
+
+### 体系结构
+### 物理层
+### 数据链路层
+### 网络层
+### 传输层
+### 应用层
\ No newline at end of file
diff --git a/docs/manuscripts/ds/basic_introduction.md b/docs/manuscripts/ds/basic_introduction.md
deleted file mode 100644
index 60abf4e..0000000
--- a/docs/manuscripts/ds/basic_introduction.md
+++ /dev/null
@@ -1,317 +0,0 @@
----
-title: 基础入门
----
-
-## 基础概念
-
-### 数据
-
-**信息的载体**,是客观事物属性的数、字符以及所有能够输入到计算机包中并且被计算机程序识别和处理的**集合**
-
-### 数据元素
-
-**数据的基本单位**,通常按照一个整数来进行考虑和处理。
-
-
-特别注意:一个数据元素由若干个**数据项**组成,数据项是构成数组元素的最小单位,且不可分割。
-
-
-
-### 数据对象
-
-具有**相同性质**的数据元素的**集合**,**是数据的子集**
-
-
-### 数据类型
-
-值的集合和定义在此集合上一组操作的总称
-
-- 原子类型:不可再分的数据类型;
-- 结构类型:可以分解成若干分量(成分)的数据类型;
-- **抽象数据类型**:抽象出具组织和其相关的操作;
-
-
-
-### 抽象数据类型(ADT)
-
-> Tips: 可以结合高级语言中类对象封装来理解;
-
-```bash
-ADT抽象数据类型名{
- 数据对象:<数据对象的定义>
- 数据关系:<数据关系的定义>
- 基本操作:<基本操作的定义>
-} ADT抽象数据类型名
-```
-
-
-
-一个数学模型以及定义在该模型上的一组操作。定义仅仅取决于它的一组逻辑操作。与计算机内部如何表示和实现是没有关系;
-
-**不论内部结构如何变化,只要其数学特性不变,就不会影响到外部的使用,实现了数据封装和信息隐藏**
-
-
-通常由(数据对象、数据关系、数据操作集)三元组来表示抽象数据类型;
-
-抽象数据类型的主要作用是**数据封装和信息隐藏,让实现与使用相分离**。数据及其相关操作的结合称为数据封装。对象可以对其他对象隐藏某些操作细节,从而使这些操作不会受到其他对象的影响。
-
-抽象数据类型独立于运算的具体实现,使用户程序只能通过抽象数据类型定义的某些操作来访问其中的数据,实现了信息隐藏。
-
-
-### 数据结构
-
-
-首先明确:数据元素都不是孤立存在的。元素与元素之间存在着某种关系,这种相互之间的关系就是**结构**。
-
-**数据结构是相互之间存在一种或者多种特定关系的数据元素的集合**
-
-- 逻辑结构
-- 存储结构(物理结构)
-- 数据运算
-
-数据的逻辑结构和存储结构是密不可分的。
-
-**算法的设计取决于所选定的逻辑结构;算法的实现依赖于所采用的存储结构;**
-
-
----
-
-## 数据结构三要素
-
-- 数据的逻辑结构
-- 数据的存储结构
-- 数据的运算
-
-
-### 数据的逻辑结构
-
-数据元素之间的逻辑关系,从逻辑关系上描述数据,叫做数据的逻辑结构。
-
-与数据的存储(物理)结构无关,是独立于计算机的。
-
-可以分为:
-
-- 线性结构
-- 非线性结构
-
-
-线性表是典型的线性结构,衍生出的栈、队列、串、数组、广义表也都是线性结构;
-
-非线性结构主要有:集合、树(一般树、二叉树)、图(有向图、无向图)
-
-
-特别注意:
-
-- `集合`:结构中的数据元素之间**除了“同属于一个集合”的关系外,别无其他关系。**
-- `线性结构`:结构中的数据元素之间**只存在一对一的关系**。
-- `树形结构`:结构中的数据元素之间**存在一对多的关系。**
-- `图状结构和网状结构`:结构中的数据元素之间**存在多对多的关系。**
-
-
-
-### 数据的存储(物理)结构
-
-数据结构在计算机中的表示(映像)。包括数据`元素的表示`和`关系的表示`。
-
-存储结构是逻辑结构用计算机语言实现的,依赖于计算机语言。
-
-
-可以分为:
-
-- 顺序存储
-- 链式存储
-- 索引存储
-- 散列(Hash)存储
-
-**注意:存储和存取的概念不一样**
-
-#### 顺序存储
-
-**逻辑上相邻的元素存储在物理位置上也相邻的存储单元里,元素之间的关系由存储单元的邻接关系来体现。**
-
-优点:
-
-- 可以实现随机存取
-- 元素占用最少的存储空间
-
-缺点:
-
-- 只能使用相邻的一整块存储单元,依赖于物理结构相邻;
-- 容易产生`外部碎片`
-
-什么是内外部碎片?
-
-> 参考资料:https://blog.csdn.net/qq_22238021/article/details/80209062
-
-- 外部碎片:`还没有分配出去`(不属于任何进程),但是**由于大小而无法分配给申请内存空间的新进程的内存空闲块。**
-- 内部碎片:`已经被分配出去`(能明确指出属于哪个进程)的**内存空间大于请求所需的内存空间,不能被利用的内存空间就是内部碎片。**
-
-
-#### 链式存储
-
-与顺序存储不同,**链式存储不要求逻辑上相邻的元素在物理位置上也相邻。**
-
-借助指示元素存储地址的`指针`表示元素之间的逻辑关系。
-
-
-优点:
-
-- 不会出现碎片现象
-- 充分利用所有存储单元
-
-缺点:
-
-- 除了存储元素外,还需要额外存储指针,会占用额外的存储空间(结合数据库索引学习)。
-- 链式存储,**只能实现`顺序存取`,不能实现`随机存取`(指针的遍历)**
-
-
-#### 索引存储
-
-存放数据元素和元素间关系的存储方式,在存储元素信息的同时,还需要建立附加的`索引表`。
-
-**索引表的每一项称为索引项,索引项的一般形式是:<关键字,地址>**
-
-优点:
-
-- 检索快(就好比字典有了目录,查询就很快了)
-
-
-缺点:
-
-- 增加了索引表,占用较多的存储空间(典型的空间换时间策略)
-- 增加、删除数据时,需要对应修改索引表,花费更多时间。
-
-#### 散列(Hash)存储
-
-根据元素的关键字直接通过散列(Hash)函数计算出元素的存储地址。
-
-
-优点:
-
-- 检索快,添加、删除元素结点操作快(获取元素地址直接,整体时间就少了)
-
-
-缺点:
-
-- 非常依赖于`散列函数`
-- 会出现`散列冲突`(主要依赖与散列函数,散列函数不好就很容易出现散列冲突)
-- 出现`散列冲突`时,解决冲突就会增加时间和空间上的开销
-
-
-### 数据的运算
-
-数据上的运算包括:`运算的定义` 、`运算的实现`
-
-- `运算的定义`:针对逻辑结构,指出运算的功能
-- `原酸的实现`:针对存储结构,指出运算的具体操作步骤
-
-
-
-
-线性表既可以用顺序存储方式实现,也可以用链式存储方式实现。
-
-
----
-
-## 算法和算法评价
-
-
-### 算法
-
-`算法` : 对特定问题求解步骤的一种描述,**是指令的有序集合**,每一条指令表示一个或多个操作。
-
-
-#### 重要特性
-
-- `有穷性`:必须总是(对任何合法的输入值)在**执行有穷步后结束**,并且每一步都可**在有穷时间内完成**
-- `确定性`:每条指令的含义明确,不会产生二义性(歧义),**对相同的输入只能得出相同的结果**
-- `可行性`:算法是可行的。**算法中描述的操作都是可以通过已经实现的基本运算执行有限次来实现的**
-- `输入`:有零个或者多个输入,**输入取决于某个特定的对象的集合。**
-- `输出`:有一个或者多个输出,**输出是和输入有着某种特定关系的量(强调输出与输入的关系)**
-
-
-> **算法是有穷的,但是程序不一定满足有穷性**,程序只是算法在计算机上的特定的实现, 例如:死循环
-
-#### 算法的目标
-
-由于设计思路、解决问题方案等方面不同,不同算法之间也是有好坏的,就像人与人之间存在着差异。为设计出更好的算算法,往往需要追求更高的目标,而好的算法需要考虑到的目标就有:
-
-- 正确性:首先算法肯定是**需要正确的解决求解问题**
-- 可读性:**算法应该具有良好的可读性**,就像项目代码一样,好的业务代码、逻辑清楚,**便于理解**。
-- 健壮性:**在输入非法数据时,算法也能适当地做出反应或进行处理,而不会产生莫名奇妙的输出结果**(在高级语言编程中,类似于强调封装方法的参数校验)
-- 效率与低存储量需求:**效率即算法执行的时间**,**存储量需求即算法那执行过程中所有要的最大存储空间**,这些与算法所解决问题的规模有关;
-
-
-> Tips: 效率可以结合时间复杂度来理解,存储量需求可以结合空间复杂度理解;
-
-
-### 效率的度量
-
-算法效率的度量是通过`时间复杂度`和`空间复杂度`来描述的;
-
-#### 时间复杂度
-
-语句的频度:语句在算法中被重复执行的次数
-
-算法中所有语句的`频度之和`记作T(n),即:对应算法问题规模n的函数,时间复杂度主要是来分析T(n)的数量级;
-
-**算法的时间复杂度不仅依赖于问题的规模n,也取决于待输入的数据的性质(例如:输入元素的初始状态)**
-
-上面这句话是不是不能理解??? 哈哈哈,我第一次看,也是!!
-
-```c
-
-int test(n) {
- if(n< 1){
- return 0;
- }
- // 循环叠加 输出
- .....
-}
-
-
-
-```
-在这个简单的函数里
-
-- 当n<1的时候,例如:-2,就不需要循环,此时时间复杂度可以理解为T(1)
-- 当n>1的时候,例如:5 此时时间复杂度可以理解为T(n)
-
-当然,这里只是简单举例子便于理解:
-
-> **算法的时间复杂度不仅依赖于问题的规模n,也取决于待输入的数据的性质(例如:输入元素的初始状态)**
-
-
-
-- `最坏时间复杂度`:**最坏情况下**,算法的时间复杂度
-- `平均时间复杂度`:**所有可能输入实例在同等概率出现的情况下**,算法的期望运行时间
-- `最好时间复杂度`:**最好的情况下**,算法的时间复杂度
-
-
-一般情况下,考虑最坏情况的时间复杂度(即:最坏时间复杂度),保证算法的运行时间不会更长(最糟糕我都能预料,难道还有更糟糕?????噗呲)
-
-#### 空间复杂度
-
-算法的空间复杂度可以用函数记作:S(n),**用来定义算法运行过程中需要耗费的存储空间**,是问题规模n的函数;
-
-> 渐进空间复杂度也被称为空间复杂度,记作:S(n)=O(g(n))
-
-
-**一个程序除了需要存储空间来存放本身所用的指令、常数、变量和输入数据外,也需要对数据进行操作的工作单元和存储一些实现计算所需要信息的辅助空间。**
-
-当输入数据所占用的空间只取决于问题本身,和算法无关时,只需要去分析除了输入和程序之外的额外空间
-
-
-
-算法原地工作:算法所需要辅助空间是常量,记作S(1),例如:
-
-```c
-int switchValue(a,b){
- // 定义临时变量
- int temp=a;
- b=temp;
- a=b;
-}
-```
-
-在上面的函数中,只是通过临时变量temp来实现a和b的值交换,没有需要更多变量,因此可以简单理解函数的在`原地工作`,辅助空间是常量,记作S(1)
diff --git a/docs/manuscripts/ds/coding/algorithm.md b/docs/manuscripts/ds/coding/algorithm.md
deleted file mode 100644
index fd35f48..0000000
--- a/docs/manuscripts/ds/coding/algorithm.md
+++ /dev/null
@@ -1,4 +0,0 @@
-
-# 算法恶补
-
->todo
\ No newline at end of file
diff --git a/docs/manuscripts/ds/ds.sidebar.ts b/docs/manuscripts/ds/ds.sidebar.ts
index 1af8e34..ea5cf97 100644
--- a/docs/manuscripts/ds/ds.sidebar.ts
+++ b/docs/manuscripts/ds/ds.sidebar.ts
@@ -58,7 +58,7 @@ export const dsSidebar = [
collapsible: false,
children: [
{
- text: '栈-基本概念和基本操作',
+ text: '栈-基本概念和操作',
link: '1.栈的基本概念和基本操作.md'
},
{
diff --git a/docs/manuscripts/ds/linear-table/1.basic_concept_and_operation.md b/docs/manuscripts/ds/linear-table/1.basic_concept_and_operation.md
index 8645d8e..8b2d5bb 100644
--- a/docs/manuscripts/ds/linear-table/1.basic_concept_and_operation.md
+++ b/docs/manuscripts/ds/linear-table/1.basic_concept_and_operation.md
@@ -1,4 +1,4 @@
-# 线性表的基础概念和基本操作
+# 线性表的基础概念和操作
> 强调线性表是一种逻辑结构,不是存储结构
diff --git a/docs/manuscripts/ds/linear_table.md b/docs/manuscripts/ds/linear_table.md
deleted file mode 100644
index e7a9ae7..0000000
--- a/docs/manuscripts/ds/linear_table.md
+++ /dev/null
@@ -1,317 +0,0 @@
-
-# 线性表
-
-## 基础概念和基本操作
-
-> 强调线性表是一种逻辑结构,不是存储结构
-
-
-### 定义
-
-线性表是具有相同数据类型的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
-
-```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 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
\ No newline at end of file
+在线预览
+
diff --git a/docs/manuscripts/mark-map/cn-map.md b/docs/manuscripts/mark-map/cn-map.md
index 3a4b07c..362d223 100644
--- a/docs/manuscripts/mark-map/cn-map.md
+++ b/docs/manuscripts/mark-map/cn-map.md
@@ -1,11 +1,4 @@
# 计算机网络
-
\ No newline at end of file
+在线预览
+
diff --git a/docs/manuscripts/mark-map/ds-map.md b/docs/manuscripts/mark-map/ds-map.md
index 5c429ce..7993c14 100644
--- a/docs/manuscripts/mark-map/ds-map.md
+++ b/docs/manuscripts/mark-map/ds-map.md
@@ -1,11 +1,1606 @@
# 数据结构
-
\ No newline at end of file
+在线预览
+
+
+
+
+
+## 基础入门
+
+### 基本概念
+
+- 数据
+
+ - 信息的载体
+ - 客观事物属性的数、字符以及所有能够输入到计算机包中并且被计算机程序识别和处理的集合
+
+- 数据元素
+
+ - 数据的基本单位
+ - 一个数据元素由若干个数据项组成
+ - 数据项是构成数组元素的最小单位,且不可分割
+
+- 数据对象
+
+ - 具有相同性质的数据元素的集合
+ - 数据的子集
+
+- 数据类型
+
+ - 原子类型:不可再分的数据类型
+ - 结构类型:可以分解成若干分量(成分)的数据类型
+ - 抽象数据类型:抽象出具组织和其相关的操作
+
+- 抽象数据类型(ADT)
+
+ - 定义:一个数学模型以及定义在该模型上的一组操作
+ - 特点:与计算机内部如何表示和实现是没有关系
+ - 作用:数据封装和信息隐藏,让实现与使用相分离
+ - 注意:不论内部结构如何变化,只要其数学特性不变,就不会影响到外部的使用
+
+### 数据结构(三要素)
+
+- 逻辑结构
+
+ - 线性结构:线性表、栈、队列、串、数组...
+ - 非线性结构:树、图、集合...
+ - 集合:除了“同属于一个集合”的关系外,别无其他关系。
+ - 线性结构:只存在一对一的关系。
+ - 树形结构:存在一对多的关系。
+ - 图状结构和网状结构:存在多对多的关系。
+
+- 存储(物理)结构
+
+ - 顺序存储
+
+ - 逻辑上相邻的元素存储在物理位置上也相邻的存储单元里,元素之间的关系由存储单元的邻接关系来体现
+ - 优点
+
+ - 可以实现随机存取
+ - 元素占用最少的存储空间
+
+ - 缺点
+
+ - 只能使用相邻的一整块存储单元,依赖于物理结构相邻
+ - 容易产生外部碎片
+
+ - 链式存储
+
+ - 与顺序存储不同,链式存储不要求逻辑上相邻的元素在物理位置上也相邻
+ - 优点
+
+ - 不会出现碎片现象
+ - 充分利用所有存储单元
+
+ - 缺点
+
+ - 除了存储元素外,还需要额外存储指针,会占用额外的存储空间(结合数据库索引学习)
+ - 链式存储,只能实现顺序存取,不能实现随机存取(指针的遍历)
+
+ - 索引存储
+
+ - 存放数据元素和元素间关系的存储方式,在存储元素信息的同时,还需要建立附加的索引表
+ - 优点
+
+ - 检索快(就好比字典有了目录,查询就很快了)
+
+ - 缺点
+
+ - 增加了索引表,占用较多的存储空间(典型的空间换时间策略)
+ - 增加、删除数据时,需要对应修改索引表,花费更多时间
+
+ - 散列(Hash)存储
+
+ - 根据元素的关键字直接通过散列(Hash)函数计算出元素的存储地址。
+ - 优点
+
+ - 检索快
+ - 添加、删除元素结点操作快(获取元素地址直接,整体时间就少了)
+
+ - 缺点
+
+ - 非常依赖于散列函数
+ - 会出现散列冲突(主要依赖与散列函数,散列函数不好就很容易出现散列冲突)
+ - 出现散列冲突时,解决冲突就会增加时间和空间上的开销
+
+- 数据的运算
+
+ - 运算的定义:针对逻辑结构,指出运算的功能
+ - 运算的实现:针对存储结构,指出运算的具体操作步骤
+
+### 算法与算法评价
+
+- 算法
+
+ - 定义
+
+ - 对特定问题求解步骤的一种描述
+ - 是指令的有序集合
+ - 每一条指令表示一个或多个操作
+
+ - 五大特性
+
+ - 有穷性:执行有穷步后结束、在有穷时间内完成
+ - 确定性:每条指令的含义明确,不会产生二义性,对相同的输入只能得出相同的结果
+ - 可行性:算法中描述的操作都是可以通过已经实现的基本运算执行有限次来实现的
+ - 输入:有零个或者多个输入,输入取决于某个特定的对象的集合
+ - 输出:有一个或者多个输出,输出是和输入有着某种特定关系的量
+
+ - 好算法追求的目标
+
+ - 正确性:需要正确的解决求解问题
+ - 可读性:便于理解
+ - 健壮性:在输入非法数据时,算法也能适当地做出反应或进行处理,而不会产生莫名奇妙的输出结果
+ - 效率与低存储量需求
+
+ - 效率:算法执行的时间--->时间复杂度
+ - 存储量需求:算法那执行过程中所有要的最大存储空间--->空间复杂度
+
+- 算法的评价
+
+ - 程序语句频度:程序语句在算法中被重复执行的次数
+ - 时间复杂度O(n)
+
+ - 最坏空间复杂度:最坏情况下,算法的时间复杂度
+ - 平均空间复杂度:所有可能输入实例在同等概率出现的情况下,算法的期望运行时间
+ - 最好空间复杂度:最好的情况下,算法的时间复杂度
+
+ - 空间复杂度S(n)
+
+ - 用来定义算法运行过程中需要耗费的存储空间
+ - 渐进空间复杂度也被称为空间复杂度
+ 记作:S(n)=O(g(n))
+
+## 线性表
+
+### 定义和基本操作
+
+- 基本概念
+
+ - 定义:线性表是具有相同数据类型的n(n≥0)个数据元素的有限序列
+ - 逻辑特性
+
+ - 除表头元素外,线性表中每个元素有且仅有一个
+ - 除表尾元素外,线性表中每个元素有且仅有一个直接后继
+
+ - 基本特点
+
+ - 元素个数有限
+ - 逻辑上具有顺序性
+ - 每个元素都是单个元素
+ - 数据类型都相同,元素存储空间大小一致
+ - 元素具有抽象性,只讨论逻辑关系
+
+- 基本操作
+
+ - 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开始的,数组中的元素的下标是从0开始的
+
+ - 存储分配
+
+ - 静态分配:数组的大小和空间都是实现确定好的,一旦存储空间占满就会产生溢出,直接导致程序崩溃
+ - 动态分配:存储数据的空间在程序执行过程中通过动态存储分配语句分配的,即便是数据空间占满,也可以另外开辟一块更大的空间,来替换原来的存储空间
+ - malloc()函数: 指针型函数,返回的指针指向该分配域的开头的位置。作用是在内存的动态存储区中分配一个长度为size的连续空间
+ - 动态分配不是链式存储,而是属于顺序存储结构,动态分配的物理结构没有改变,依然是随机存取的方式。只是分配的空间大小可以在运行时决定
+
+ - 重要特点
+
+ - 随机访问【重要】
+ - 存储密度高
+ - 逻辑上相邻的元素物理上也相邻
+
+- 基本操作
+
+ - 插入元素
+
+ - 在顺序表L的第i(1≤i≤L.length+1)个位置插入新元素,成功返回true,失败返回false
+ - 时间复杂度
+
+ - 最好情况:在表尾插入,元素向后移动循环没有执行,时间复杂度O(1);
+ - 最坏情况:在表头插入,元素后移循环执行n次,时间复杂度为O(n);
+ - 平均情况:随机插入,平均次数为:n/2,对应的平均复杂度为O(n);
+
+ - 平均时间复杂度为:O(n)
+
+ - 删除元素
+
+ - 删除顺序表L中第i(1≤i≤L.length+1)个位置的元素。成功则返回true,将被删除的元素用引用变量返回,失败则返回false
+ - 时间复杂度
+
+ - 最好情况:删除表尾元素,不需要移动任何元素,时间复杂度为O(1);
+ - 最坏情况:删除表头元素,需要移动除第一个元素外的所有元素,时间复杂度为O(n)
+ - 平均情况:随机删除,平均需要(n-1)/2,对应的时间复杂度为O(n)
+
+ - 平均时间复杂度为O(n)
+
+ - 按值查找(顺序查找)
+
+ - 在顺序表L中查找第一个元素值等于e的元素查找成功返回该元素位序(不是角标),查找失败返回0
+ - 位序(个人理解):元素在线性表中的位置序号,角标为i(角标从0开始),对应的位序为i+1(位序从1开始)。当返回为0时,则直接代表没有命中
+ - 时间复杂度
+
+ - 最好情况:查找的元素在表头,只需要比较一次,循环成本最小,时间复杂度为O(1);
+ - 最坏情况:查找的元素在表尾或者不存在,需要完整遍历,比较n次,时间复杂度为O(n);
+ - 平均情况:随机查找表上的第i个(1≤i≤L.length)元素,平均次数为(n+1)/2,对应时间复杂度为O(n)
+
+ - 平均时间复杂度为O(n)
+
+### 链式表示
+
+- 基础理解
+
+ - 链式存储线性表时,不需要使用连续的存储单元,不要求逻辑上相邻的两个元素在物理位置上也相邻
+ - 链式存储线性表时,对线性表的插入、删除元素是不需要移动元素的,只是需要修改指针
+ - 理解“链”的含义,链条--->捆绑、指向------>指针
+
+- 单链表
+
+ - 定义:线性表的链式存储称作单链表,通过一组任意的存储单元来存储线性表中的数据元素。
+ - 每个链表结点(node)除了存放元素自身的信息外,还需要存放一个指向其后继结点的指针。通过指针建立起链表元素之间的线性关系
+ - 单链表可以解决顺序表需要大量连续存储空间的缺点,但是单链表在数据域的基础上附加了指针域,存在浪费存储空间的缺点
+ - 单链表的元素是离散地分布在存储空间中的,不能直接找到表中特定的结点,需要从头开始遍历、一次查找【单链表是非随机存取的存储结构】
+ - 头结点相关
+
+ - 定义:为了操作上的方便,在单链表第一个结点之前附加一个结点,叫做头结点。
+ - 基本特点
+
+ - 不论单链表是否带头结点【可选】,头指针始终指向链表的第一个结点
+ - 头结点的指针域指向线性表的第一个元素结点
+ - 头结点的数据域可以不存任何信息、也可以记录表长等基础信息
+ - 头结点是带头结点的链表中的第一个结点
+
+ - 优点
+
+ - 因为开始结点的位置被存放在头结点的指针域中,所以在链表的第一个位置上的操作和在表的其他位置上的操作一致,不需要进行特殊处理
+ - 无论链表是否为空,头指针始终是指向头结点的头结点的非空指针
+ - 头结点的引入,很好的统一了空表和非空表的操作,有效避免头指针空指针异常问题
+
+ - 基本操作
+
+ - 链表创建
+
+ - 头插法
+
+ - 定义:从空表开始,生成新的结点,将读取的数据存放在新结点的数据域中,将新结点插入到当前链表的表头【头结点之后】
+ - 读入数据的顺序与生成的链表中的元素顺序是相反的
+ - 每个结点插入的时间复杂度为O(1),单链表长度为n时,头插法的时间复杂度为O(n)
+
+ - 尾插法
+
+ - 定义:新结点插入到当前链表的表尾上,必须增加一个尾指针r,始终指向当前链表的尾结点
+ - 读入数据的顺序与生成的链表中的元素顺序完全一致
+ - 每个结点插入的时间复杂度为O(1),单链表长度为n时,尾巴插法的时间复杂度为O(n)
+ - 相比头插法附设了一个指向表尾结点的指针,但时间复杂度与头插法相同
+
+ - 按序号查找
+
+ - 定义:在单链表中从第一个结点出发,顺指针next域逐个往下搜索、遍历,直到找出第i个结点为止,否则返回最后一个结点指针域NULL
+ - 时间复杂度为:O(n)
+
+ - 按值查找
+
+ - 定义:从单链表的第一个结点开始,从前往后依次比较表中个结点数据域的值,等于给定值e,则返回该结点的指针;若整个单链表【遍历完】中没有数据域值为e的结点,则返回NULL;
+ - 时间复杂度为:O(n)
+
+ - 插入结点
+
+ - 定义:单链表中,将值为x的新结点插入到单链表的第i个位置上
+ - 基本步骤
+
+ - 第一步: 检查插入位置的合法性;
+ - 第二步: 找到待插入位置的前驱结点,即第(i-1)个结点;
+ - 第三部: 在前驱结点后插入新结点;
+ - 过程不能更换,避免后继指针不存在的问题
+
+ - 时间复杂度
+
+ - 插入结点的时间复杂度集中在查找第(i-1)个元素,时间复杂度为O(n)
+ - 如果在给定结点的后面插入新结点,只需要执行p->next=s操作,时间复杂度为O(1)
+
+ - 前插操作:在某结点的前面插入一个新的结点
+ - 后插操作:在某结点的后面插入一个新的结点,单链表插入算法中,通常采用后插操作的
+ - 对结点的前插操作都可以转化为后插操作,前提:需要从单链表的头结点开始顺序查找到其前驱结点;时间复杂度为O(n)。
+
+ - 删除结点
+
+ - 定义:将单链表L的第i个结点元素删除;
+ - 基本步骤
+
+ - 第一步: 先检查删除位置的合法性;
+ - 第二步: 查找表中的第(i-1)个结点,即被删结点的前驱结点;
+ - 第三步: 移动指针,删除结点元素;
+
+ - 和插入算法一样,时间都消耗在查询前驱结点上,时间复杂度为:O(n)
+ - 利用p结点的后继结点将p结点删除【经典思路】
+
+ - 第一步:申请结点q,使其只想p结点的后继结点;
+ - 第二步:将p结点的数据域值换成其后继结点的数据域;【注意,交换没什么意义,最终p的后继结点会删除、释放】
+ - 第三步:p的指针域指向q的指针域,q结点从链中“断开”
+ - 第四步:释放q的内存空间
+ - 相比按值查找前驱结点来删除给定的结点p,利用后继结点来删除的时间复杂度更小,为:O(1)
+
+
+ - 计算表长(遍历)
+
+ - 定义:计算单链表中数据结点(不含头结点)的个数
+ - 算法思路:从第一个结点开始顺序依次访问表中的每一个结点,为此需要设置一个`计数器变量`,每访问一个结点,计算器加1,直到访问到空结点为止。
+ - 时间复杂度:O(n)
+ - 判空条件
+
+ - 不带头结点的单链表L为空,判定条件是L=NULL。
+ - 带头结点的单链表L为空,判空条件:L->next=NULL
+
+- 双链表
+
+ - 定义:在单链表的结点中增加了一个指向结点前驱的`prior`指针,由prior指针域、data数据域、next指针域三部分组成。
+ - 基本特点
+
+ - 双链表仅仅在单链表的结点中增加了一个指向结点前驱的prior指针
+ - 按值查找、按序号查找在单链表和双链表上的操作是相同的
+ - 和单链表不同,插入、删除操作除了修改next指针域,双链表还需要修改prior指针域,确保不断`链`【重要】
+
+ - 基本操作
+
+ - 插入结点
+
+ - 第一步:s->next=p->next
+ - 第二步:p->next-prior=s
+ - 第三步:s->prior=p
+ - 第四步:p->next=s
+ - 时间复杂度:O(1)
+ - 注意:结点p和s的前驱、后继指针要关联清楚,第一二步必须在第四步之前完成【重要】
+
+ - 删除结点
+
+ - 第一步:p->next=q->next
+ - 第二步:q->next->prior=p
+ - 第三步:free(q) 释放结点内存空间
+ - 时间复杂度:O(1)
+ - 注意:删除双链表结点p的后继结点的第一二步,顺序可换,及时释放内存空间【重要】
+
+- 循环链表
+
+ - 循环单链表
+
+ - 定义:在单链表的基础上,将最后一个结点(尾结点)的指针由`NULL`改为指向`头结点`,形成`环`。【单链表----->循环单链表】
+ - 判断条件
+
+ - 不是判断头结点的指针是否为空,而是需要判断是否等于头指针
+ - 表为空时,头结点的next指针域其实是指向自己
+ - 结点的next指针指向自己,也就能判断表为空
+
+ - 基本特点
+
+ - 在循环单链表中,尾结点*p的next指针域指向链表L(即:头结点),形成了`闭环`,不存在指针域为`NULL`的结点
+ - 由于循环单链表是个`环`,在任何位置上的插入、删除操作都是等价的,不需要去判断是否是表尾
+ - 单链表只能从头结点(表头结点)开始往后顺序遍历整个表,循环单链表可以从表中任意位置开始遍历整个链表,结点是等价的
+ - 循环单链表可以抽象为时钟,形成的`环`是有顺序的
+ - 频繁的`表头`和`表尾`操作,可以对循环单链表设置`尾指针`,而不设置`头指针`,明确尾指针r后,头指针即为:`r->next` ,减少头指针到尾指针间的遍历,时间复杂度:O(n)---->O(1)
+
+ - 循环双链表
+
+ - 定义:在双链表的基础上,将尾结点的next指针指向头结点,将头结点的prior指针指向尾结点。【双链表----->循环双链表】
+ - 判空条件
+
+ - 循环双链表为空时,头结点*p的prior指针和next指针都指向L
+ - 同时满足
+
+ - p->prior=L
+ - p->next=L
+
+ - 基本特点:从双向链表中的任意一个结点开始,都可以很方便地访问它的`前驱结点`和`后继结点`
+
+- 静态链表
+
+ - 定义:借助数组来描述线性表的链式存储结构,结点元素同样存在数据域`data`和指针域`next`
+ - 需要注意
+
+ - 和普通的链表的指针域不同的是,静态链表的指针是结点元素的相对地址(数组下标),也称为`游标`,建议结合高级语言中数组的概念来理解;
+ - 与顺序表一样,虽然静态链表属于链表,但是存储时需要预先分配一块连续的内存空间
+ - 静态链表是通过`数组游标`来访问下一个结点元素
+
+ - 特点
+
+ - 静态链表以`next=-1`作为结束的标志【尾结点】
+ - 和动态链表相同,插入、删除操作不需要移动元素,只需要修改指针;
+ - 总体来说,静态链表没有单链表使用方便,需要将整个链表存储在一块连续的内存空间中,内部的存储可以分散,通过指针构成`链`的关系
+
+- 零碎知识补充
+
+ - 无论是链表的插入还是删除操作,必须保证不断链【重要】
+ - 顺序存储结构可以随机存取也能顺序存取,链式结构只能进行顺序存取
+ - 顺序存储方式同样适合图和树的存储,例如:满二叉树的顺序存储
+ - 队列需要在表头删除元素,在表尾插入元素【先进先出】,采用带尾指针的循环单链表比较方便
+ - 静态链表中的指针称为`游标`,指示下一个元素在数组中的`下标`
+ - 静态链表用数组表示,需要预先分配较大的连续空间,同时具有一般链表的特点(插入、删除元素不需要移动元素)
+ - 单链表设置头结点的
+
+ - 目的:方便运算
+ - 好处
+
+ - 有头结点后,插入、删除数据元素的算法统一起来了,不需要判断是否在第一个元素之前插入或者删除第一个元素了
+ - 不论链表是否为空,头指针是指向头结点的`非空指针`,链表的头指针不变,因此空链表和非空链表的处理也就统一起来了。
+
+### 顺序表和链表的比较
+
+- 存取方式
+
+ - 顺序表支持顺序存取和随机存取
+ - 链表只能从表头顺序存取元素,支持顺序存取
+
+- 逻辑结构与物理结构
+
+ - 顺序存储时,逻辑上相邻的元素,对应的物理存储位置也相邻【一定性】
+ - 链式存储时,逻辑上相邻的元素,对应的物理存储位置不一定相邻【可以相邻,也可以不相邻】
+ - 链式存储的逻辑关系通过指针链接表示
+
+- 时间复杂度
+
+ - 按值查找
+
+ - 顺序表无序的情况下,顺序表和链表的时间复杂度均为O(n);
+ - 顺序表有序的情况下,顺序表的时间复杂度为O(log2n),链表为O(n);
+
+ - 按序号查找
+
+ - 顺序表支持随机访问,时间复杂度为O(1);
+ - 顺序表不支持随机访问,时间复杂度为O(n);
+
+ - 插入、删除
+
+ - 顺序表平均需要移动半个表长的元素;
+ - 链表只需要修改相应结点的指针域,不需要移动元素;
+ - 链表结点除了数据域,还有指针域,在存储空间上比顺序存储需要更大的存储空间,付出更大的存储代价,存储密度不够大
+
+- 空间分配
+
+ - 顺序存储
+
+ - 静态分配
+
+ - 需要预先分配足够大的存储空间
+ - 空间装满后不能扩充,存储新元素将出现`内存溢出`
+ - 存储空间过大,顺序表后部闲置空间过多,造成`内部碎片`
+ - 存储空间过小,会造成`内存溢出`
+
+ - 动态分配
+
+ - 能够扩充存储空间
+ - 需要移动大量元素,操作效率降低
+ - 内存中没有更大块的连续存储空间,将会导致空间分配失败;
+
+ - 链式存储
+
+ - 结点空间只在需要的时候申请分配
+ - 只要内存由空间就可以分配,操作灵活、高效
+
+### 存储结构选取
+
+- 基于存储的考虑
+
+ - 对线性表的长度和存储规模难以估计时,不宜采用顺序表存储
+ - 链表不用事先估计存储规模,但存储密度较低
+ - 链式存储结构的存储密度小于1,不要求连续的存储空间
+
+- 基于运算的考虑
+
+ - 随机存取
+
+ - 顺序表支持随机存取,按序号查找顺序表的时间复杂度为O(1),相对较好
+ - 链表不支持随机存取,按序号查找链表的时间复杂度为O(n)
+
+ - 插入、删除操作
+
+ - 顺序表的插入、删除操作,平均需要移动表中一半的元素,当表的数据量较大时,这种情况需要重点考虑的
+ - 链表的插入、删除操作,也是需要找插入位置(前驱结点、后继结点),主要的操作还是比较操作,相对较好
+
+- 基于环境的考虑
+
+ - 顺序表容易实现,任何高级语言中都有数组类型
+ - 链表操作是基于指针的,指针移动,相对复杂
+
+- 综合结论
+
+ - 通常比较稳定的线性表选择顺序存储
+ - 频繁进行插入、删除操作的线性表,应该选择链式存储,动态性较强
+
+## 栈和队列
+
+### 栈【后进先出】
+
+- 基础概念
+
+ - 定义: 只允许在一端进行插入或者删除操作的线性表。
+
+ - 后进先出
+ - 明确栈是一种线性表
+ - 限定栈只能在某一端进行插入或者删除操作
+
+ - 栈顶:线性表允许进行插入和删除的一端
+ - 栈底:不允许进行插入和删除的另外一端,是固定的
+ - 空栈:不含任何元素的空表,也叫栈空
+
+- 基本操作
+
+ - InitStack(&S): 初始化一个空栈S,栈顶指针初始化为-1
+ - StackEmpty(S): 判断一个栈是否为空,如果栈空则返回true,否则返回false
+ - Push(&S,x): 进栈,若栈未满,x进栈操作,插入到栈内成为新的栈顶元素
+ - Pop(&S,&x): 出栈,若栈非空,出栈操作,弹出栈顶元素,用指针x进行返回
+ - GetTop(S,&x): 读栈顶元素,若栈S非空,用x返回栈顶元素
+ - ClearStack(&S): 销毁栈,释放栈S占用的存储空间
+
+- 顺序存储结构
+
+ - 顺序栈
+
+ - 定义:栈的顺序存储,利用一组地址连续的存储单元存放自栈底到栈顶的所有元素,同时**附加一个用来指向当前栈顶位置的指针**
+ - 顺序栈S【重要,栈顶指针初始化为-1】
+
+ - 栈顶指针S.top
+
+ - 初始时,S.top=-1
+ - 栈顶元素:S.data[S.top]
+
+ - 进栈操作:栈不满时,栈顶+1,再进栈
+ - 出栈操作:栈非空时,取栈顶元素,栈顶-1
+ - 栈满条件:S.top=MaxSize-1
+ - 栈空条件:S.top=-1
+ - 栈长:S.top+1
+ - 当对栈的最大使用空间估计不足时,容易出现栈上溢(外溢),需要主动向用户报告反馈,避免出现错误;
+
+ - 栈顶指针初始化为0【更多是为-1】
+
+ - 入栈: `S.data[S.top++]=x`
+ - 出栈: `x=S.data[--S.top]`
+ - 同时, 栈空、栈满条件也会有变化
+
+ - 基本运算
+
+ - `InitStack(&S)`: 初始化一个空栈`S`,栈顶指针初始化为-1
+ - `StackEmpty(S)`: 判断一个栈是否为空,即:栈顶指针是否为-1,如果栈空则返回`true`,否则返回`false`
+ - `Push(&S,x)`: 进栈,若栈未满,`x`进栈操作,插入到栈内成为`新的栈顶元素`。
+ - `Pop(&S,&x)`: 出栈,若栈非空,出栈操作,**弹出栈顶元素**,用指针`x`进行返回。
+ - `GetTop(S,&x)`: 读(获取)栈顶元素,若栈`S`非空,用x返回栈顶元素。
+
+ - 共享栈
+
+ - 定义:利用栈底位置相对不变的特性,可以让两个顺序栈共享一个`一维存储空间`,将两个栈的栈底分别设置在共享空间的两端,两个栈顶则向共享空间的中间延伸
+ - 基础概念
+
+ - 两个栈(0、1号顺序栈)的栈顶指针都指向栈顶元素
+ - 0号栈栈顶指针`top=-1`时,0号栈为空
+ - 1号栈栈顶指针`top=MaxSize`时,1号栈为空
+ - 当且仅当两个栈的栈顶指针相邻(`top1-top0=1`),可以判断共享栈栈满
+
+ - 进栈【先移动指针,后赋值】
+
+ - 当0号栈进栈时,0号栈栈顶指针top0`先加1后赋值`
+ - 当1号栈进栈时,0号栈栈顶指针top1`先减1后赋值`
+
+ - 出栈【先赋值,后移动指针】
+
+ - 当0号栈进栈时,0号栈栈顶指针top0`先赋值后减1`
+ - 当1号栈进栈时,0号栈栈顶指针top1`先赋值后加1`
+
+ - 重要特性
+
+ - 共享栈能够更有效的利用存储空间,两个栈空间进行相互调节
+ - 只有当这个存储空间(即:共享空间)被占满时才会发生上溢
+ - 共享栈对存取效率没有什么影响,存取数据的时间复杂度都为O(1),在栈顶操作。
+
+- 链式存储结构
+
+ - 基本概念
+
+ - `链栈`: 采用链式存储的栈
+ - `栈满`:对于链栈来说,是基于链式存储的,基本不存在栈满的情况,除非内存已经没有使用空间了
+ - `栈空`:对于空栈来说,链表原来的定义是头指针指向空,那么链栈的空其实就是`top=NULL`,链栈元素总数为0
+ - 通常对于链栈来说,是不需要头结点的,当然也存在带头结点的链栈
+
+ - 基础操作【基于单链表】
+
+ - 入栈
+
+ - 如果链栈不存在,则栈满,入栈操作失败,返回false;
+ - 如果链栈存在,进行单链表的结点插入操作,移动指针,结点元素赋值,再将结点压入链栈中,移动链栈栈顶指针,最后链栈元素总数+1,返回true
+
+ - 出栈
+
+ - 如果链栈不存在,或者为空栈,则无法进行出栈操作,返回false
+ - 如果链栈满足出栈条件,则通过栈顶指针获取到链栈栈底结点,将其数据域赋值给变量e,移动栈顶指针指向待出栈元素的后继结点,同时释放待出栈元素的内存空间,链栈元素总数-1 ,出栈成功,返回true.
+
+ - 基于单链表的链栈入栈、出栈操作,时间复杂度都为O(1)【重要】
+
+ - 优点
+
+ - 便于多个栈共享存储空间
+ - 不存在栈满上溢的情况,避免程序因溢出导致出错
+ - 有效的提高存取效率
+
+### 队列【先进先出】
+
+- 基本概念
+
+ - 队列:和栈一样,是一种操作受限制的线性表,只允许在表的一端进行插入,在表的另外一端进行删除,简称为`队`,常记作:`Queue`
+ - 队头:允许进行删除操作的一端,也叫做`队首`,常记作:`Front`
+ - 队尾:允许进行插入操作的一端,常记作:`Rear`
+ - 空队列:不含任何元素的空表,注意这个表是指`线性表`
+ - 入队: 向队列中插入元素,也叫做`进队`
+ - 出队: 删除队列元素,也叫做`离队`
+
+- 基础操作
+
+ - InitQueue(&Q): 初始化一个队列,构造空队列Q
+ - `QueueEmpty(Q)`: 判断队列是否为空,队空返回true,否则返回false
+ - EnEmpty(&Q,x): 入队,如果队列Q未满,将x入队,成为新的队尾元素
+ - DeEmpty(&Q,&x): 出队,如果队列Q非空,删除队头元素,复制给x返回
+ - GetHead(Q,&x): 读取队头元素,如果队列Q非空,则将队头元素赋值给x
+
+- 顺序存储
+
+ - 顺序队列
+
+ - 队列的顺序实现是指分配一块连续的存储单元用来存放队列中的元素,并且附加两个指针
+
+ - front指针: 指向队头元素的位置
+ - rear指针: 指向队尾元素的位置
+
+ - 初始状态(队空条件):Q.front===Q.rear===0
+ - 入队操作:队不满时,先赋值给队尾元素,再移动队尾指针+1
+ - 出队操作: 队不空时,先取队头元素值,再移动队头指针+1
+ - Q.front===Q.rear===0,那能用Q.rear==MaxSize来表示队满嘛?【不行】
+
+ - 循环队列
+
+ - 把顺序队列臆想为一个环状的空间,将存储队列元素的表从逻辑上看做为一个环
+ - 初始时:Q.front=Q.rear=0
+ - 队首指针进1: Q.front=(Q.front+1)%MaxSize
+ - 队尾指针进1: Q.rear=(Q.rear+1)%MaxSize
+ - 队列长度: (Q.rear+MaxSize-Q.front)%MaxSize
+ - 除法取余运算(%)【解决顺序队列“上溢出”】问题
+ - 如何区别队空还是队满?
+
+ - 方案一:牺牲一个单元来区分队空和队满
+
+ - 前提:队头指针在队尾指针在队尾指针的下一个位置作为队满标志【重要】
+ - 队满条件:(Q.rear+1)%MaxSize==Q.front
+ - 队空条件:Q.front==Q.rear
+ - 队列中元素个数:(Q.rear+MaxSize-Q.front)%MaxSize
+
+ - 方案二:类型中增设表示元素个数的数据成员
+
+ - 直接和MaxSize去比较
+ - 队空条件: Q.count=0
+ - 队满条件: Q.count=MaxSize
+ - 【注意】不论是队空还是队满,对会存在Q.front=Q.rear,这个可以通过前面方案一解决
+
+ - 方案三:类型中增设tag数据成员标记
+
+ - 通过添加tag标记的方式,区分队空还是队满
+ - tag==0的情况下,如果因为删除导致Q.front==Q.rear,则队空;
+ - tag==1的情况下,如果因为插入导致Q.front==Q.rear,则队满;
+ - tag的主要作用
+
+ - 在有元素入队的时候,设置tag=1
+ - 在有元素出队的时候,设置tag=0
+
+- 链式存储
+
+ - 链队列
+
+ - `链队列`:和顺序队列一样,基于队列的链式表示叫做`链队列`,实际上为:**一个同时带有队头指针和队尾指针的单链表**
+
+ - 头指针指向队头结点
+ - 尾指针指向队尾结点(单链表的最后一个结点)
+
+ - 不带头结点链式队列
+
+ - 队空: linkQueue.front==NULL且linkQueue.rear==NULL
+ - 出队: 先判断队列是否为空,非空队列则取出队头元素,从链表中闪出去,队头指针Q.front指向下一个结点,如果出队的结此为尾结点,出队后队空,需要将Q.front和Q.rear都置为NULL
+ - 入队: 建立一个新的结点,将新的结点插入到链表的尾部,修改队尾指针Q.rear指向新插入的结点。如果原队列为空,需要将队首指针也指向该结点
+ - 【入队、出队操作,都需要考虑队空的情况下的特殊处理,不带头结点的队列导致队空队首和队尾指针都为NULL,麻烦】
+
+ - 带头结点的链式队列
+
+ - 【复杂的入队、出队操作就统一起来】
+ - 队空:Q.front==Q.rear,都指向头结点,一般数据域可以为空
+ - 出队:判断队列是否为空,队列非空则在队首移动指针,将队首指针指向下一个元素。如果队列中就一个元素,则出队后将成为空队,Q.rear==Q.front,最后释放元素内存空间。
+ - 入队:将元素插入队尾,移动队尾指针,即便为空队列入队,由于队列带有头结点,此时就很好的避免操作队首指针了。
+ - 子主题 5
+
+ - 特别注意
+
+ - 用单链表表示的链式队列非常适合频繁出队、入队、元素变化大的场景
+ - 不存在队满情况,也不会出现溢出情况;
+ - 链式队列不会出现存储分配不合理、“溢出”的情况,内存动态分配
+
+ - 基本操作【带头结点】
+
+ - 队列初始化
+ - 判断队空:Q.front==Q.rear
+ - 入队【重要】
+ - 出队【重要】
+
+ - 双端队列
+
+ - 允许在两端都可以进行入队和出队操作的队列,元素的逻辑结构仍然是线性结构
+ - 双端队列的两端分别称为前端和后端,两端都可以入队和出队
+
+ - 进队:前端进的元素排列在队列中后端进的元素的前面,后端进的元素排列在队列前端进的元素后面;
+ - 出队:无论是前端还是后端出队,先出的的元素排列在后出的元素的前面
+
+ - 输入受限的双端队列:允许在一端进行插入和删除操作,但在另外一端只允许进行删除的双端队列
+ - 输出受限的双端队列:允许在一端进行插入和删除曹组,但在另外一端只允许进行插入的双端队列
+
+ - 如果限定双端队列从某个断点插入的元素只能从该端点删除,那么此时的双端队列就演变为两个栈底相邻的栈
+
+ - 补充
+
+ - 最适合用来链队的链表是:带队首指针和队尾指针的非循环单链表
+ - 栈和队列的逻辑结构都是线性表,存储结构可能是顺序的(顺序栈、顺序队列),也可能是链式的(链栈、链队)
+ - 不论是顺序存储还是链式存储,栈和队列都只能进行顺序存取(本质是线性表)。数组是可以做到随机存取(本质是顺序表)
+ - 队列先进先出的特性:先进队列的元素先出队列,后进队列的元素后出队列
+
+### 应用
+
+- 【栈】括号匹配
+- 【栈】表达式求值
+- 【栈】递归算法
+
+ - 定义: 如果在一个函数、过程或数据结构的定义中又应用了自身,那么这个函数、过程或者数据结构称为递归定义的,简称递归。
+ - 递归通常把一个大型的复杂问题,层层转化为一个与原问题相似的规模较小的问题来求解
+ - 斐波拉切数列算法优化
+
+ - 使用非递归实现
+ - 使用空间换效率,例如:数组
+ - 使用缓存,存放数据
+
+ - 必须注意递归模型不能是循环定义,
+ 需要满足两个条件
+
+ - 递归表达式(递归体)
+ - 边界条件(递归出口),即:算法结束条件
+
+ - 特点
+
+ - 很大程度上减少了程序的代码量
+ - 算法效率不高【重要特点】
+ - 调用次数过多容易造成栈溢出
+
+- 【队列】层次遍历
+- 【队列】计算机系统
+
+ - 解决主机和外部设备之间速度不匹配的问题
+ - 解决由多用户引起的资源竞争问题
+
+### 特殊矩阵的压缩存储
+
+- 在`计算机图形学`、`工程计算`中占有举足轻重的地位。
+- 数组定义:由n(n≥1)个相同类型的数据元素构成的有限序列。
+- 重要概念
+
+ - 【压缩存储】多个值相同的元素只分配一个存储空间,对零元素不分配存储空间---->节省存储空间。
+ - 【特殊矩阵】具有很多相同矩阵元素或零元素,并且这些相同矩阵元素或零元素的分布有一定规律性的矩阵。
+ - 【稀疏矩阵】矩阵元素个数s相对于矩阵中非零元素的个数t来说非常多、差距非常大,即`s>>t的矩阵`可以叫`稀疏矩阵`
+
+- 常见的特殊矩阵
+
+ - 对称矩阵
+ - 上、下三角矩阵
+ - 对角矩阵(带状矩阵)
+ - ......
+
+- 注意
+
+ - 常规方法来存储稀疏矩阵,会想当浪费存储空间,所以稀疏矩阵只需要存储非零元素
+ - 通常非零元素的分布是没有规律的,除了存储非零元素外,还需要存储元素所在位置的行和列
+ - 寻相互存储三元组 `<行标,列表,值>`
+ - 三元组的结点存储了行标(row)、列表(col)、值(value)三种信息,是主要用来存储稀疏矩阵的一种数据结构
+
+- 数组和线性表的关系
+
+ - 数组是线性表的推广
+ - 数组一旦被定义,维数和维界就不再改变
+ - 除了结构的初始化和销毁外,数组只会有存取元素和修改元素的操作
+
+- 数组的存储结构
+
+ - 按行优先
+ - 按列优先
+
+## 树与二叉树
+
+### 树
+
+- 定义
+- 术语
+
+ - 结点
+
+ - 祖先结点
+ - 父子结点
+ - 兄弟结点
+ - 孩子结点
+ - 子孙结点
+ - 分支结点
+ - 叶子结点(终端结点)
+
+ - 度
+
+ - 结点的度
+ - 树的度
+
+ - 结点的深度、高度、层次
+ - 有序树和无序树
+ - 路径和路径长度
+ - 森林
+
+- 性质
+
+### 二叉树
+
+- 定义和特性
+- 存储结构
+- 二叉树遍历
+- 线索二叉树
+
+### 树和森林
+
+- 存储结构
+- 树、森林与二叉树转换
+- 遍历
+- 并查集应用
+
+### 应用
+
+- 二叉排序树
+- 平衡二叉树
+- 哈夫曼树
+- 哈夫曼编码
+
+## 图论
+
+### 定义
+
+### 基本操作
+
+- Adjacent(G,x,y)
+- Neighbors(G,x)
+- InsertVertex(G,x)
+- DeleteVertex(G,x)
+- AddEdge(G,x,y)
+- RemoveEdge(G,x,y)
+- FirstNeighbor(G,x)
+- NextNeighbor(G,x,y)
+- Get_edge_value(G,x,y)
+- Set_edge_value(G,x,y,v)
+
+### 存储及操作
+
+- 邻接矩阵法
+- 邻接链表法
+- 十字链表
+- 邻接多重表
+
+### 图的遍历
+
+- 广度优先搜索(BFS)
+- 深度优先搜索(DFS)
+- 连通性
+
+### 图的应用
+
+- 最小生成树(MST)
+- 最短路径
+- 拓扑排序
+- 关键路劲
+
+## 查找
+
+### 基本概念
+
+- 查找:在数据集合中寻找满足某种条件的数据元素的过程
+- 查找表(查找结构):用于查找的数据集合
+
+ - 数据元素(记录)的类型相同
+ - 可以是一个数组或者链表等数据类型
+ - 常见操作
+
+ - 查询某个特定元素是否存在
+ - 检索满足条件的特定元素的各种属性
+ - 数据元素插入
+ - 数据元素删除
+
+- 静态查找表:不需要动态地修改查找表,与动态查找表对应
+
+ - 顺序查找
+ - 折半查找
+ - 散列查找
+ - ....
+
+- 动态查找表:需要动态地插入或者删除的查找表
+
+ - 二叉排序树的查找
+ - 散列查找
+ - ....
+
+- 关键字:数据元素中唯一标识该元素的某个数据项的值
+
+ - 使用关键字查找,查找结果应该是【唯一的】
+ - 可以类比集合中不重复的key
+ - 是不是想起了数据库的主键的概念????
+
+- 平均查找长度:所有查找过程中进行关键字的比较次数的平均值
+
+ - 查找长度:一次查找需要比较的关键字次数
+ - 【是衡量查找算法效率的最主要的指标】
+
+### 顺序查找(线性查找)
+
+- 主要用于在线性表中进行查找
+
+ - 一般的【无序线性表】的顺序查找
+ - 按关键字【有序的顺序表】的顺序查找
+ - Tips:顺序表是指顺序存储的线性表,前面的有序才强调元素有序,顺序强调的是存储方式
+
+- 基于一般线性表的顺序查找
+
+ - 最直观的查找方式
+ - 基本思想:从线性表的一端开始,逐个查询条件和关键字进行比对即可
+ - 【重要】:哨兵的引入,可以避免很多不必要的判断语句,提高程序的效率
+ - 平均查找长度
+
+ - 查找成功
+
+ - 第i个元素需要进行(n-i+1)次关键字比较
+ - 概率相等的情况下:(n+1)/2
+
+ - 查找失败【没有找到元素】
+
+ - 各关键字比较次数:(n+1)
+ - 平均查找长度:(n+1)
+
+ - 综合分析
+
+ - 【缺点】:n较大时,平均查找长度较大,效率低
+ - 【优点】:对数据元素的存储没有要求,顺序存储和链式存储都可行。对数据元素的有序性也没有要求
+ - 【注意】:线性的链表只能进行顺序查找
+
+ - 通常情况下,查找表中记录的查找概率并不相等
+
+- 基于有序表的顺序查找
+
+ - 预先已经知道表是按照关键字有序的,基于【二叉判定树】来查找,不用全部遍历----->【降低顺序查找失败的平均查找长度】
+ - 平均查找长度
+
+ - 查找成功
+
+ - 和一般线性表的顺序查找一样
+ - 概率相等的情况下:(n+1)/2
+
+ - 查找失败
+
+ - 到达是失败结点所查找的长度=失败结点父结点所在的层数【建议画画二叉判定树】
+ - 查找概率相等时,平均查找长度:n/2 + n/(n+1)
+
+ - 【注意】:有序表的顺序查找中的线性表可以是链式存储结构的
+
+### 折半查找(二分查找)
+
+- 【注意】仅仅适用于有序的顺序表
+- 基本思想:首先与表中间位置元素的关键字比较,相等则查找成功,不相等则向左|向右继续与该部分中间元素比较......
+- 核心思路:左右双指针,互相往中间靠拢,可以用二叉判定树来辅助思考
+- 平均查找长度【计算非常重要】
+
+ - 查找成功
+
+ - 折半查找法查到给定值的比较次数最多不会超过【判定树】的高度
+ - 相等概率下,平均查找长度约等于log2(n+1) -1 ,【建议结合二叉树高度计算来理解】
+
+ - 查找失败直接看例题.....
+
+- 综合分析
+
+ - 折半查找的时间复杂度为O(log2n), 平均情况下笔顺序查找O(n)的效率高
+ - 折半查找需要方便地定位查找区域---->存储结构必须具有【随机存取】的特性
+ - 【注意】仅仅适用于有序的顺序表,不适用链式存储结构,要求表按关键字有序排列
+
+### 分块查找(索引顺序查找)
+
+- 具备顺序查找和折半查找的优点,既有动态结构,又能适用于快速查找
+- 是不是有点【希尔排序】和【直接插入排序、折半插入排序】的发展效果?????
+- 基本思想:查找表分成若干个子块
+
+ - 块内元素可以无序,【块之间必须有序】
+ - 前一块的最大关键字永远小于后一块的最小关键字
+ - 【重要】:建立索引表,包含每个分块额最大关键字和第一个元素的地址(起始角标)
+ - 索引表按关键字有序排列
+
+- 查找方式
+
+ - 索引表:顺序查找或者折半查找
+ - 块内:顺序查找<----- 块内可以无序
+
+- 【直接看书】平均查找长度=索引查找平均长度+块内查找的平均长度
+
+### 【直接看书|视频,这部分我很迷糊】B树和B+树
+
+- B树(多路平衡查找树)
+
+ - 基础概念
+
+ - 阶:所有结点的孩子结点数的最大值
+ - m阶B树(可能为空树)满足
+
+ - 树中每个结点至多有m棵子树(即至多包含m-1个关键字)
+ - 如果根结点不是终端结点,至少有两棵子树
+ - 所有的叶结点都出现在同一个层次上,不带任何信息
+ - ....
+
+ - B树是所有结点的平衡因子均为0的多路查找树
+
+ - 基本操作
+
+ - 高度
+
+ - B树的大部分操作需要的磁盘存取次数和B树的高度成正比
+ - B树的高度不包括最后的不带任何信息的叶结点所处的那一层【有的参考书,也包含这部分】
+ - 【重要】如果每个结点重点额关键字个数达到最少,则容纳同样多关键字的B树的高度可以达到最大
+ - ....
+
+ - 查找
+
+ - 步骤
+
+ - 在B树中找结点【磁盘中进行】
+ - 在结点内找关键字【内存中进行】----> 采用【顺序查找法】或【折半查找法】
+ - 【Tips】B树常存储在磁盘上,在磁盘上找到目标节点后,将结点中的信息读入到内存
+
+ - 当查找到叶子结点,对应的指针为空指针,即树中没有对应的关键字
+
+ - 插入
+
+ - 插入操作比查找操作复杂多
+ - 步骤
+
+ - 定位
+
+ - B树查找算法--->最底层中某个非叶结点
+ - 【重要】B树的插入关键字一定是插入到最底层的某个非叶结点内
+
+ - 插入
+
+ - 插入结点关键字个数小于m----> 直接插入
+ - 插入后检查插入结点内关键字个数,【大于m-1必须进行分裂】
+
+ - 【直接看书】】注意分裂的方法
+
+ - 删除(删除的关键字在终端结点)
+
+ - 直接删除关键字
+ - 兄弟够借
+ - 兄弟不够借
+
+- B+树的概念
+
+ - B+树是根据数据库的需要需要的一中B树的变形树
+ - m阶的B+树需要满足的条件
+
+ - 每个分支结点最多有m棵子树(子结点)
+ - 结点的字树个数与关键字相等
+ - 非叶、根结点至少有两棵子树,其他分支结点至少有m/2(向上取整)棵子树
+ - 所有叶结点包含全部关键字及指向相应记录的指针
+
+ - 叶结点按关键字大小排列
+ - 相邻结点按大小顺序相互链接起来
+
+ - 【反复理解】所有的分支结点(理解为索引的索引)中仅仅包含它的各个子结点(下一级的索引快)中关键字的最大值和指向其子结点的指针
+ - ....
+
+### 散列(Hash)表
+
+- 基本概念
+
+ - 散列函数:一个把查找表中的关键字映射成该关键字对应的地址(数组下标、索引、内存地址等)的函数
+ - 散列冲突:散列函数把两个或者多个不同关键字映射到同一个地址上,【冲突总是不可避免的】
+ - 同义词:发生散列冲突(碰撞)的不同关键字
+ - 散列表:根据关键字直接访问的数据结构,是关键字和存储地址之间的一种直接映射关系
+ - 理想情况下,散列表中查找时间复杂度为O(1),与元素个数无关
+ - 基于【比较】的查找算法,查找效率取决于比较的次数
+ - 以上可以结合哈希加密、布隆过滤器等工作中的编程概念去比对学习....
+
+- 散列函数
+
+ - 构造散列函数,需要注意
+
+ - 散列函数的定义域【必须】要包含全部需要存储的关键字,值域的范围则依赖于散列表的大小或地址范围
+ - 散列函数计算出来的地址应该能【等概率、均匀的】分布在整个存储空间,尽可能减少散列冲突---> 【压测】
+ - 散列函数应该尽量简单,能够在较短时间内就计算出任一关键字对应的散列地址
+
+ - 常用散列函数
+
+ - 直接定址法
+
+ - 直接去关键字的某个【线性函数值】为散列函数
+ - 优点
+
+ - 计算简单
+ - 【不会产生冲突】
+ - 适合关键字的分布【基本连续】的情况
+
+ - 缺点:关键字分布不连续时,空位较多,将造成存储空间的浪费
+
+ - 除留余数法
+
+ - 最简单、最常用的散列方法
+ - 关键在于【选择被除数P】,等概率进行映射,尽可能减少冲突的可能性
+
+ - 数字分析法
+
+ - 适用于已知的关键字集合
+ - 如果更换了关键字,就需要重新构造新的散列函数
+
+ - 平方取中法
+
+ - 取关键字的平方值的中间几位来作为散列地址
+ - 适用于关键字的每一位取值都不够均匀或小于散列地址所需要的位数
+
+ - 折叠法
+
+ - 将关键字分割成位数相同的几部分,取这几部分的叠加作为散列地址
+ - 关键字位数很多,每一位上数字分布大致均匀时,可以采用折叠法得到散列地址
+
+ - .....
+
+ - 不同的情况,不同的散列函数会发挥不同的性能,无法笼统的说那种散列函数最好。散列函数的目标都是为了将产生冲突的可能性尽可能地降低
+
+- 处理冲突
+
+ - 【任何设计出来的散列函数都不可能绝对地避免冲突】,必须考虑在发生冲突时如何进行处理
+ - 处理方法
+
+ - 开放定址法
+
+ - 概念:可以存放新表项的空闲地址,既向它的同义词表项开放,也向它的非同义词表项开放
+ - 增量取值方法
+
+ - 线性探测法
+ - 平方探测法
+ - 再散列法(双散列法)
+ - 伪随机序列法
+
+ - 注意事项
+
+ - 不能随便【物理删除】表中已有元素,删除将会截断具有相同散列地址元素的查找地址,牵涉元素广
+ - 删除元素需要【单独做标记】,采用【逻辑删除】
+ - 逻辑删除会导致散列表表面上看上满的,实际上很多位置都是没有被利用的,【需要定期维护,将删除标记的元素物理删除】
+
+ - 拉链(链接)法
+
+ - 概念:为避免非同义词发生冲突,可以把所有的同义词存储在一个线性链表中【由散列地址唯一标识】
+ - 【重要】适用于经常进行插入和删除的情况
+
+ - 拉链法处理冲突时不存在聚集现象,用线性探测法处理冲突时容易产生聚集现象
+
+- 性能分析
+
+ - 装填因子:一个表的装满程度 =(表中的记录数)/ (散列表的长度)
+ - 查找效率取决于
+
+ - 散列函数
+ - 处理冲突的方法
+ - 【重要】装填因子
+
+ - 散列表的平均查找长度依赖于【散列表的填装因子】
+ - 填装因子越大,表示装填的越“满”,发生冲突的可能性就越大
+ - 散列冲突导致散列表在查找过程中也是需要进行比较的。【查找效率仍然用平均查找长度来度量】
+ - 冲突的产生概率与装填因子(表中记录数和表长)的大小成正比
+
+### 模式匹配(字符串)
+
+- 简单模式匹配
+
+ - 概念:第一个字符串(模式串)在第二串(主串)中的位置
+ - 基本过程:从主串指定字符开始(一般第一个)和模式串的第一个字符逐个比较....
+ - 时间复杂度:O(n*m),n、m分别为主串和模式串的长度
+
+- 【难点,直接看代码理解】KMP算法
+
+ - 【重要】是对简单模式匹配的改造,时间复杂度在O(n+m)的数量集上
+ - 【建议手动模拟】基本过程
+
+ - 整个匹配过程,没有进行指针回溯
+ - 每趟比较过程让子串向后滑动到一个合适位置, 让这个位置上的字符和主串中的那个字符比较
+ - 【重要】next数组的求解实际是对某个位置找到最长的公共前缀
+
+- 总结比较
+
+ - 简单模式匹配时间复杂度:O(n*m)
+ - KMP算法的时间复杂度为O(n+m)
+ - 一般情况下,简单模式匹配的实际执行时间可以近似到O(n+m)
+ - 【重要】KMP算法的重要特点是主串指针不回溯
+
+## 排序
+
+### 基本概念和定义
+
+- 排序:重新排列表中的元素,让表中的元素能够满足按照关键字递增或者递减的过程
+- 算法的稳定性【非常重要】
+- 排序算法是否具有稳定性并不能衡量一个算法的优劣【重要】
+- 内部排序:在排序期间元素全部存放在内存中的排序
+
+ - 关键字比较
+ - 移动元素
+ - 不是所有的内部排序算法都是基于比较操作的,例如:基数排序属于内部排序算法,但不是基于比较实现的
+
+- 外部排序:在排序期间元素无法全部同时存放在内存中,必须在排序的过程中根据要求不断地在内、外存之间移动的排序。
+
+### 插入排序
+
+- 基本思想:每次将一个待排序的记录,按关键字大小插入到前面已经排序好的子序列中,直到全部记录插入完成
+- 直接插入排序
+
+ - 最简单、最直观的插入排序算法
+ - 性能分析
+
+ - 空间效率:仅仅使用到了常数个辅助单元,空间复杂度为O(1)
+ - 时间效率:排序过程中,需要向左侧有序子表中逐个插入元素,操作n-1次,每次操作都分为关键字比较和元素移动这两部分的次数非常依赖于待排序表的初始状态【重要】
+
+ - 最好的情况:元素已经有序,每个元素之需要比较一次,不用移动元素,O(n)
+ - 最坏的情况:元素逆序,比较多次,移动多次,O(n^2)
+ - 平均情况:总的比较次数和总的移动次数均约等于为(n^2)/4
+
+
+ - 稳定性: 【稳定】的排序算法
+ - 适用性
+
+ - 顺序存储的线性表
+ - 链式存储的线性表
+ - 大部分排序算法都仅仅适用于顺序存储的线性表【重要】
+
+- 折半插入排序
+
+ - 简述
+
+ - 直接插入:边比较边移动
+
+ - 确定插入位置
+ - 腾出空间,元素复制到插入位置
+
+ - 折半插入:先比较再统一移动
+
+ - 确定好待插入的位置后,再统一地向后移动元素
+
+ - 性能分析
+
+ - 折半插入排序的比较次数与待排序表的初始状态无关,仅仅取决于表中的元素个数n
+ - 移动元素的次数相比直接插入排序没有任何的改变
+ - 直接插入排序和折半插入排序的比较次数一样,依赖于排序表的初始状态
+ - 时间复杂度:O(n^2)
+ - 稳定性:【稳定】的排序算法
+
+- 希尔排序(缩小增量排序)
+
+ - 基本思想:将待排序表分割成为若干个L[i,i+d,i+2d,....,i+kd]的子表,分别进行直接插入排序,当整个表元素“基本有序”的时候,再对全体记录进行一次直接插入排序
+ - 基本实现步骤
+
+ - 第一步:取一个小于n的步长d1 ,把待排序的表分成d1个组,所有距离为d1的倍数的记录放在同一个组中,对各组进行直接插入排序
+ - 第二步:取第二个步长d2 < d1,重复第一步
+
+ - 主要操作
+
+ - 确认步长,分组
+ - 对分组元素进行直接插入排序
+
+ - 性能分析
+
+ - 空间效率:仅仅使用到了常数个辅助单元,O(1)
+ - 时间效率
+
+ - 直接插入排序
+
+ - 是顺序的,时间复杂度最最小,O(n)
+ - 是逆序的,时间复杂度最大,O(n^2)
+ - 是局部有序的,即部分顺序、部分逆序,此时的时间复杂度介于两者之间,O(n)~O(n^2)
+
+ - 取决于增量序列函数
+
+ - 优于折半插入排序
+ - 最坏情况为O(n^2)
+
+ - 稳定性:【不稳定】,存在相同值元素分在不同的组进行直接插入排序
+ - 适用性:仅仅适用于顺序存储的线性表,虽然采用过程中有采用直接插入排序,但增量序列不为1的时候,需要随机存取,链式存储的时候无法满足
+
+### 交换排序
+
+- 冒泡排序
+
+ - 算法简单、思路直接、十分常用但考查少
+ - 两元素交换方案
+
+ - 临时变量法
+ - 加减法
+
+ - 空间效率
+
+ - 仅使用了常数个辅助单元
+ - 空间复杂度为O(1)
+
+ - 时间效率
+
+ - 最好情况(顺序)
+
+ - 比较次数:n-1
+ - 移动次数:0
+ - 时间复杂度为:O(n)
+
+ - 最坏情况(逆序)
+
+ - 比较次数:每趟比较(n-i)次
+ - 移动次数:每趟移动3次
+ - 时间复杂度为:O(n^2)
+
+ - 时间复杂度为:O(n^2)
+
+ - 稳定性:【稳定】
+
+- 快速排序
+
+ - 基于递归、理解困难、分而治之、关键基准pivot划分
+ - 空间效率
+
+ - 基于递归实现,需要借助递归工作栈来保存每一次递归调用的必要信息
+ - 最坏情况:进行n-1次递归,O(n)
+ - 最好情况: log2(n+1) 向上取整
+ - 平均情况: O((n+1)以2为底的对数)
+
+ - 时间效率
+
+ - 运行时间与划分操作Partition()函数【不对称相关】
+ - 最坏情况
+
+ - 数组长度为n
+ - 左侧快排的数组长度为n-1,有n-1个元素【初始排序表基本有序】
+ - 右侧快排的数组长度为0,有0个元素【初始排序表基本逆序】
+ - 时间复杂度为:O(n^2)
+
+ - 效率优化
+
+ - 递归过程中划分得到的子序列规模较小时候,不再递归调用快速排序 ----->改用直接插入排序【时间复杂度最好O(n),性能好】
+ - 尽量选择能将数据中分的基准值元素
+ - 随机从当前表中选取基准值元素,几乎避免最坏情况的发生
+
+ - 稳定性:【不稳定】,存在值相同的元素,位置与最终位置不一致情况
+ - 重要:【是所有内部算法中平均新能最优的排序算法】
+
+### 选择排序(TBD)
+
+- 简单选择排序
+- 堆排序
+
+### 归并排序(TBD)
+
+### 基数排序
+
+- 很特别的排序算法,【不是基于比较进行排序的】
+- 基本步骤【趟数取决于元素的最大位数】
+
+ - 分配:依次考察线性表元素,放入队列中
+ - 收集:各队列中结点一次首尾相接,组成新的线性表
+
+- 分类
+
+ - 最高位优先排序【MSD】
+ - 最低位优先排序【LSD】
+
+- 性能分析
+
+ - 空间效率:
+
+ - 一趟排序需要r个队列的辅助存储空间,后续排序这些队列将会复用
+ - 空间复杂度为:O(n)
+
+ - 时间效率
+
+ - 进行d趟分配和收集操作
+
+ - 一趟分配需要O(n)
+ - 一趟收集需要O(r)
+
+ - 平均情况:时间复杂度为O(d*(n+r))
+ - 【重要】算法的时间效率与初始排序表状态无关,依赖于分配和收集操作
+
+ - 稳定性:【稳定】<----- 按位排序必须稳定
+
+### 内部排序
+
+- 各算法性能比较
+
+ - 从时间复杂度来看
+
+ - 平均情况O(n^2)
+
+ - 直接插入排序
+ - 简单选择排序
+ - 冒泡排序
+
+ - 最好情况O(n)【顺序情况】
+
+ - 直接插入排序
+ - 冒泡排序
+
+ - O(nlog2n)
+
+ - 归并排序与初始序列的排序列无关,所有情况下时间复杂度都是O(nlog2n)
+ - 堆排序利用数据结构堆,线性时间完成建堆,在O(nlog2n)内完成排序
+ - 快排平均性能可以达到O(nlog2n)【性能常常优于其他排序算法】
+
+ - 基于分治法的思想
+
+ - 快速排序
+ - 归并排序
+
+ - 简单选择排序与序列的初始值状态无关
+ - 希尔排序是插入排序的拓展,在大规模排序中可以达到很高的效率n^1.3~2
+
+ - 从空间复杂度来看
+
+ - 借助常数个辅助空间
+
+ - 简单选择排序
+ - 插入排序
+ - 冒泡排序
+ - 希尔排序
+ - 堆排序
+
+ - 快速排序在空间上只适用一个小的辅助栈,实现【递归】
+
+ - 平均情况:O(log2n)
+ - 最坏情况:O(n)
+
+ - 二路归并排序需要较多的辅助空间,为O(n),可以采取时间换空间的思路来减少辅助空间【不建议,导致算法复杂】
+
+ - 从稳定性来看
+
+ - 稳定的
+
+ - 插入排序
+ - 冒泡排序
+ - 归并排序
+ - 基数排序
+
+ - 不稳定
+
+ - 简单选择排序
+ - 快速排序
+ - 希尔排序
+ - 堆排序
+
+ - 从过程特征来看
+
+ - 冒泡排序和堆排序每次循环都能拿到当前的最大值或最小值
+ - 快速排序每次循环都能确定一个元素到最终位置上
+ - 冒泡排序每一趟冒泡也能确定一个元素到最终位置上
+
+- 算法运用
+
+ - 排序方法选取需要考虑的因素
+
+ - 待排序的元素数目n
+ - 元素本身信息量大小
+ - 关键字的结构和分布情况
+ - 稳定性的要求
+ - 语言工具、存储结构、辅助空间大小
+
+ - 排序算法小结
+
+ - n很小时选择
+
+ - 直接插入排序
+ - 简单插入排序
+
+ - 关键字基本【有序】时选择
+
+ - 直接插入排序
+ - 冒泡排序
+
+ - n较大时选择O(nlog2n)的排序
+
+ - 快速排序【不稳定】
+ - 堆排序【不稳定】
+ - 归并排序【稳定】
+
+ - n很大且关键字位较少、可分解,建议选择【基数排序】
+ - 信息量较大、元素较多,存储结构可以采用【链表】,减少不必要的时间去元素移动
+
+ - 快速排序被认为是基于比较的内部排序中最好的排序方法
+ - 基于比较的排序算法中,每次比较两个关键字之后,只有两种情况(大于|小于)----> 二叉树, 时间复杂度至少为O(nlog2n)
+
+### 外部排序
+
+- 基本概念
+
+ - 内部排序:排序方法在内存中进行的排序
+ - 外部排序:排序过程中需要多次进行内存、外存交换,对外存文件中的记录进行排序后【仍然存放在外存原有文件中】的排序
+ - 外部排序原因:对大文件进行排序,内存控件有限,文件中信息量庞大,无法将整个文件拷贝进内存中进行排序,需要多次调入内存进行排序
+
+- 外部排序方法
+
+ - 分类【依据外存设备的不同】
+
+ - 磁盘文件排序
+ - 磁带文件排序
+
+ - 分布方式
+
+ - 磁盘是直接存取设备
+ - 磁带是顺序存取设备
+
+ - 文件通常是【按块存储】在磁盘上,操作系统也是【按块读取】磁盘上的信息
+ - 外部排序中时间代价主要考虑【访问磁盘的次数】,即I/O次数
+ - 【通常采用归并排序方法】
+
+### 补充总结和复习
+
+- 元素个数不是很大(n<1000)
+
+ - 直接插入排序【稳定】
+ - 冒泡排序【稳定】
+ - 简单选择排序【不稳定】
+ - 空间复杂度都为O(1),只需要一个辅助元素
+ - 平均时间复杂度都为O(n^2)
+
+- 中等规模的元素,希尔排序【不稳定】是非常好的选择,比直接插入排序要好(n越大越明显),不占用额外内存空间,减少直接排序次数
+- 元素个数很多(n很大)
+
+ - 快速排序【不稳定】
+
+ - 最通用、高效的内部排序算法
+ - 平均时间复杂度为O(nlog2n)
+ - 一般空间复杂度为O(log2n)
+ - 最快情况,性能退化【元素基本有序时】
+
+ - 时间复杂度提高到O(n^2)
+ - 空间复杂度提高到O(n)
+ - 解决方案: 三者(low,mid,high)取中,获取枢纽值【注意严版采用的是low,high】
+
+ - 堆排序【不稳定】
+
+ - 高效的内部排序算法
+ - 时间复杂度为O(nlog2n)
+ - 没啥最坏情况,基本不需要额外的存储空间
+
+ - 归并排序【稳定】
+
+ - 性能与初始化元素序列无关
+ - 时间复杂度总为O(nlog2n)
+ - 【明显缺点】:需要O(n)的额外存储空间
+
+ - 基数排序【稳定】
+
+ - 特殊的排序算法
+ - 除了对元素序列的关键字比较,更对关键字的不同位也进行处理和比较
+ - 具有线性增长的时间复杂度O(d*(n+r)),适用性比较低、应用场景相对少
+ - 需要额外的存储空间,一般用队列来实现桶
+
+- 【重要】不同的排序算法缓和使用,往往能够对算法进行不错的改进,获得更好的性能
+
+
diff --git a/docs/manuscripts/mark-map/note-map.sidebar.ts b/docs/manuscripts/mark-map/mark-map.sidebar.ts
similarity index 69%
rename from docs/manuscripts/mark-map/note-map.sidebar.ts
rename to docs/manuscripts/mark-map/mark-map.sidebar.ts
index 14a1fc4..8633042 100644
--- a/docs/manuscripts/mark-map/note-map.sidebar.ts
+++ b/docs/manuscripts/mark-map/mark-map.sidebar.ts
@@ -1,15 +1,23 @@
-export const noteMapSidebar = [
+/**
+ * 思维导图侧边栏
+ */
+export const MarkMapSidebar = [
{
text: '数据结构',
link: 'ds-map.md'
- }, {
+ },
+ {
text: '操作系统',
link: 'os-map.md'
- }, {
+ },
+ {
text: '计算机组成原理',
link: 'ccp-map.md'
- }, {
+ },
+ {
text: '计算机网络',
link: 'cn-map.md'
}
]
+
+
diff --git a/docs/manuscripts/mark-map/os-map.md b/docs/manuscripts/mark-map/os-map.md
index 0a069d6..2496fd4 100644
--- a/docs/manuscripts/mark-map/os-map.md
+++ b/docs/manuscripts/mark-map/os-map.md
@@ -1,11 +1,5 @@
# 操作系统
-
\ No newline at end of file
+
+在线预览
+
diff --git a/docs/manuscripts/mark-map/readme.md b/docs/manuscripts/mark-map/readme.md
index 9f1892f..ac5d332 100644
--- a/docs/manuscripts/mark-map/readme.md
+++ b/docs/manuscripts/mark-map/readme.md
@@ -1,5 +1,7 @@
# 思维导图
+> 思维导图汇总整理,内容更新慢于笔记,赶稿中...
+
- 数据结构
- 操作系统
- 计算机组成原理
diff --git a/docs/manuscripts/os/readme.md b/docs/manuscripts/os/readme.md
index 2b2908e..c302659 100644
--- a/docs/manuscripts/os/readme.md
+++ b/docs/manuscripts/os/readme.md
@@ -13,4 +13,23 @@
文件管理
I/O管理
-```
\ No newline at end of file
+```
+
+
+### 系统概述
+
+- 引论
+- 发展和分类
+- 运行环境
+- 体系结构
+
+### 进程管理
+
+- 进程基本概念和特征
+- 进程的运行
+
+### 进程管理
+
+### 文件管理
+
+### I/O管理
\ No newline at end of file
diff --git a/docs/manuscripts/readme.md b/docs/manuscripts/readme.md
index c4345f5..f4f6d7b 100644
--- a/docs/manuscripts/readme.md
+++ b/docs/manuscripts/readme.md
@@ -1,3 +1,2 @@
----
-title: 笔记
----
\ No newline at end of file
+# 笔记
+