diff --git a/README.md b/README.md index 77a4131..0bdb84d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ -
@@ -109,24 +108,23 @@
## 大纲目录(善用Ctrl+F)
- **注意:没有链接的,可以在仓库找找,只是选择性进行博客和公众号发表**
+**注意:没有链接的,可以在仓库找找,只是选择性进行博客和公众号发表**
### 数据结构
-> 参考资料:
->
-> 1. 《数据结构》 .
-> 2. 《大话数据结构》.
-> 3. 《数据结构与算法景点问题解析》 第二版 Narasimha Karumanchi著.
-> 4. 《计算机网络-王道考研》 2019 王道论坛著.
+> 参考资料
+> - 《数据结构》 .
+> - 《大话数据结构》.
+> - 《数据结构与算法景点问题解析》 第二版 Narasimha Karumanchi著.
+> - 《计算机网络-王道考研》 2019 王道论坛著.
-#### [基础入门](./manuscripts/数据结构/基础入门/readme.md)
+#### 基础入门
- [基础概念](https://mp.weixin.qq.com/s/M8MOCVnbVxQ3GRuxRaEKIg)
- [数据结构三要素](https://mp.weixin.qq.com/s/7sCoHDFtI-Qp0wBcvMH6kQ)
- [算法与算法评价](https://mp.weixin.qq.com/s/58fBAwoyg2Ali-HqOJ6t9g)
-#### [线性表](./manuscripts/数据结构/线性表/readme.md)
+#### 线性表
- [基础概念和基本操作](https://mp.weixin.qq.com/s/3e5LX9HbnjfT980zV3veXA)
- [线性表的顺序表示](https://mp.weixin.qq.com/s/gaXmF3tqZxxFXhhDyPZv-A)
@@ -139,118 +137,110 @@
- [零碎知识补充](https://mp.weixin.qq.com/s?__biz=MzU3MTUxOTIwMw==&mid=2247485549&idx=2&sn=5282cd006b6c33e4bfe992298602e053&chksm=fcdfb1e1cba838f743a335bad530cf415eef57a417b807257d8ed6983047c2d57d5552b34cb0&token=1697862929&lang=zh_CN#rd)
-#### [栈和队列](Readme.md)
+#### 栈
-- [栈的基本概念和基本操作](1.栈的基本概念和基本操作.md)
-- [栈的顺序存储结构](2.栈的顺序存储结构.md)
-- [栈的链式存储结构](3.栈链式存储结构.md)
-- [队列的基本概念和基础操作](4.队列的基本概念和基础操作.md)
-- [队列的顺序存储](5.队列的顺序存储结构.md)
-- [队列的链式存储](6.队列的链式存储结构.md)
-- [栈和队列的应用](栈和队列的应用.md)
-- [特殊矩阵的压缩存储](8.特殊矩阵的压缩存储.md)
+- 基本概念和操作
+- 顺序存储结构
+- 链式存储结构
+#### 队列
+
+- 基本概念和操作
+- 顺序存储结构
+- 链式存储结构
+
+#### 串
+
+- 基本概念
+- 简单的模式匹配
+- KMP算法
#### 树与二叉树
-- 树
- - 定义
- - 术语
- - 性质
+- 基本概念
- 二叉树
- - 定义和特性
- - 存储结构
- - 二叉树遍历
- - 线索二叉树
+- 二叉树遍历
+- 线索二叉树
- 树和森林
- - 存储结构
- - 树、森林与二叉树转换
- - 遍历
- - 并查集应用
- 树的应用
- - 二叉排序树
- - 平衡二叉树
- - 哈夫曼树
- - 哈夫曼编码
+
#### 图论
+- 基本概念
+- 图的存储
+- 图的遍历
+- 图的应用
+
+
#### 查找
+- 基本概念
+- 顺序查找
+- 折半查找
+- B树和B+树
+- 散列表
+
#### 排序
-- 排序的基础概念
-- 直接排序
-- 交换排序之冒泡排序
-- 交换排序之快速排序
+- 基本概念
+- 插入排序
+- 交换排序
+- 选择排序
- 归并排序
- 基数排序
-- 内部排序算法的比较和应用
-- 外部排序的基本概念
-- 外部排序方法整理
-
-
-
-
-### 计算机网络
-
-> 参考资料:
->
-> 1. 《计算机网络》 第七版 谢希仁著.
-> 2. 《计算机网络-王道考研》 2019 王道论坛.
-
-
-
-- [第一章:绪论]()
-- [第二章:物理层]()
-- [第三章:数据链路层]()
-- [第四章:网络层]()
-- [第五章:运输层]()
-- [第六章:应用层]()
-- [第七章:网络安全]()
-- [第八章:互联网上的音频/视频服务]()
-- [第九章:无线网络和移动网络]()
-
+- 外部排序
### 计算机操作系统
-> 参考资料:
->
-> 1. 《计算机操作系统》 第四版 汤小丹、梁红兵、汤子瀛著
-> 2. 《操作系统-王道考研》 2019 王道论坛
+> 参考资料
+> - 《计算机操作系统》 第四版 汤小丹、梁红兵、汤子瀛著
+> - 《操作系统-王道考研》 2019 王道论坛
-#### 概述
+#### 系统概述
+
+- 引论
+- 发展和分类
+- 运行环境
+- 体系结构
#### 进程管理
+
+- 进程和线程
+- 处理机调度
+- 进程同步
+- 死锁
+
+
#### 内存管理
+- 引论
+- 虚拟内存
+- 一些总结
+
#### 文件管理
+- 基本概念
+- 文件系统
+- 磁盘管理
+- 一些总结
-#### I/O管理
-- 第一章:操作系统引论
-- 第二章:进程的描述与控制
-- 第三章:处理机调度与死锁
-- 第四章:存储器管理
-- 第五章:虚拟存储器
-- 第六章:虚拟存储器
-- 第七章:文件管理
-- 第八章:磁盘存储器的管理
-- 第九章:操作系统接口
-- 第十章:多处理机操作系统
-- 第十一章:多媒体操作系统
-- 第十二章:保护与安全
+#### 输入、输出管理
+- 基本概述
+- 核心子系统
+- 高速缓存和缓冲区
+- 一些总结
### 计算机组成原理
-> 参考资料:
-> 1. 《计算机组成原理》 第二版 唐朔飞
-> 2. 《计算机组成原理-王道考研》 2019 王道论坛
+> 参考资料
+> - 《计算机组成原理》 第二版 唐朔飞
+> - 《计算机组成原理-王道考研》 2019 王道论坛
#### 概述
@@ -270,6 +260,61 @@
#### 输入输出系统
+### 计算机网络
+
+> 参考资料
+> - 《计算机网络-第七版》 谢希仁著.
+> - 《计算机网络-王道考研》 2019 王道论坛.
+
+
+#### 体系机构
+
+- 基本介绍
+- 体系结构与参考模型
+
+
+#### 物理层
+
+- 通信基础
+- 传输介质
+- 物理层设备
+
+#### 数据链路层
+
+- 基本功能
+- 组帧
+- 差错控制
+- 流量控制&可靠传输
+- 介质访问控制
+- 局域网
+- 广域网
+- 通信设备
+
+#### 网络层
+
+- 基本功能
+- 路由算法
+- 路由协议
+- IPV4
+- IPV6
+- IP组播&移动IP
+- 网络层设备
+
+#### 传输层
+
+- 提供的服务
+- UDP协议
+- TCP协议
+
+
+#### 应用层
+
+- 应用模型
+- DNS
+- FTP协议
+- 电子邮件
+- HTTP协议
+
diff --git a/docs/.vuepress/config.ts b/docs/.vuepress/config.ts
index b3ce989..c6cd461 100644
--- a/docs/.vuepress/config.ts
+++ b/docs/.vuepress/config.ts
@@ -35,6 +35,9 @@ export default defineUserConfig({
})();`
]
],
+ // 配置永久链接,参考:https://v2.vuepress.vuejs.org/zh/reference/frontmatter.html#permalink
+ // permalink: "/:year/:month/:day/:slug",
+ // permalinkPattern: `:slug.html`,
markdown: {
// todo 引入代码文件时的路径替换
importCode: {
diff --git a/docs/.vuepress/config/navbar.ts b/docs/.vuepress/config/navbar.ts
index 913b9c6..cb7fff4 100644
--- a/docs/.vuepress/config/navbar.ts
+++ b/docs/.vuepress/config/navbar.ts
@@ -6,10 +6,11 @@ export const navbar = [
text: '首页',
link: '/'
},
- {
- text: "数据结构",
- link: "/manuscripts/ds"
- },
+ // {
+ // text: "数据结构",
+ // link: "/manuscripts/ds"
+ // },
+ "/manuscripts/ds",
{
text: "操作系统",
link: "/manuscripts/os"
diff --git a/docs/.vuepress/config/plugins.config.ts b/docs/.vuepress/config/plugins.config.ts
index a0393f8..16295af 100644
--- a/docs/.vuepress/config/plugins.config.ts
+++ b/docs/.vuepress/config/plugins.config.ts
@@ -14,21 +14,21 @@ export default {
// HelloWorld: path.resolve(__dirname, '../components/HelloWorld.vue')
// }
}),
- searchProPlugin({
- // 索引全部内容
- indexContent: true,
- // 为分类和标签添加索引
- customFields: [
- {
- getter: (page: any) => page.frontmatter.category,
- formatter: "分类:$content",
- },
- {
- getter: (page) => page.frontmatter.tag,
- formatter: "标签:$content",
- },
- ],
- }),
+ // searchProPlugin({
+ // // 索引全部内容
+ // indexContent: true,
+ // // 为分类和标签添加索引
+ // customFields: [
+ // {
+ // getter: (page: any) => page.frontmatter.category,
+ // formatter: "分类:$content",
+ // },
+ // {
+ // getter: (page) => page.frontmatter.tag,
+ // formatter: "标签:$content",
+ // },
+ // ],
+ // }),
// // md文档增强
// mdEnhancePlugin({
// // 文件导入
diff --git a/docs/manuscripts/cn/readme.md b/docs/manuscripts/cn/readme.md
index 11b1fe7..7c9d6e0 100644
--- a/docs/manuscripts/cn/readme.md
+++ b/docs/manuscripts/cn/readme.md
@@ -1,10 +1,65 @@
# 计算机网络
+```mindmap
+root(计算机网络)
+ 体系机构
+ 物理层
+ 数据链路层
+ 网络层
+ 传输层
+ 应用层
+```
-### 体系结构
-### 物理层
-### 数据链路层
-### 网络层
-### 传输层
-### 应用层
\ No newline at end of file
+## 体系机构
+
+- 基本介绍
+- 体系结构与参考模型
+
+
+## 物理层
+
+- 通信基础
+- 传输介质
+- 物理层设备
+
+## 数据链路层
+
+- 基本功能
+- 组帧
+- 差错控制
+- 流量控制&可靠传输
+- 介质访问控制
+- 局域网
+- 广域网
+- 通信设备
+
+## 网络层
+
+- 基本功能
+- 路由算法
+- 路由协议
+- IPV4
+- IPV6
+- IP组播&移动IP
+- 网络层设备
+
+## 传输层
+
+- 提供的服务
+- UDP协议
+- TCP协议
+
+
+## 应用层
+
+- 应用模型
+- DNS
+- FTP协议
+- 电子邮件
+- HTTP协议
+
+## 参考资料
+
+-《计算机网络-第七版》谢希仁著.
+-《计算机网络-王道考研》2019 王道论坛.
\ No newline at end of file
diff --git a/docs/manuscripts/ds/ds.sidebar.ts b/docs/manuscripts/ds/ds.sidebar.ts
index 5c73412..2f137da 100644
--- a/docs/manuscripts/ds/ds.sidebar.ts
+++ b/docs/manuscripts/ds/ds.sidebar.ts
@@ -42,36 +42,43 @@ export const dsSidebar = [
]
},
{
- text: '栈和队列',
+ text: '栈',
prefix: '栈和队列',
collapsible: false,
children: [
{
- text: '栈-基本概念和操作',
+ text: '基本概念和操作',
link: '1.栈的基本概念和基本操作.md'
},
{
- text: '栈-顺序存储结构',
+ text: '顺序存储结构',
link: '2.栈的顺序存储结构.md'
},
{
- text: '栈-链式存储结构',
+ text: '链式存储结构',
link: '3.栈的链式存储结构.md'
- },
+ }
+ ]
+ },
+ {
+ text: '队列',
+ prefix: '栈和队列',
+ collapsible: false,
+ children: [
{
- text: '队列-基本概念和操作',
+ text: '基本概念和操作',
link: '4.队列的基本概念和操作.md'
}, {
- text: '队列-顺序存储结构',
+ text: '顺序存储结构',
link: '5.队列的顺序存储结构.md'
},
{
- text: '队列-链式存储结构',
+ text: '链式存储结构',
link: '6.队列的链式存储结构.md'
},
{
- text: '一些总结',
- link: '7.总结.md'
+ text: '栈VS队列补充',
+ link: '7.栈VS队列补充.md'
}
]
},
diff --git a/docs/manuscripts/ds/readme.md b/docs/manuscripts/ds/readme.md
index 6406d1b..859aeb1 100644
--- a/docs/manuscripts/ds/readme.md
+++ b/docs/manuscripts/ds/readme.md
@@ -1,5 +1,19 @@
# 数据结构
+```mindmap
+root(数据结构)
+ 基础入门
+ 线性表
+ 栈和队列
+ 队列
+ 串
+ 树与二叉树
+ 图论
+ 查找
+ 排序
+```
+
+
## 基础入门
- [x] 基本概念
@@ -14,7 +28,7 @@
- [x] 链式表示
- [x] 一些总结
-## 栈和队列
+## 栈
- [x] 基本概念和操作
- [x] 顺序存储结构
@@ -29,12 +43,55 @@
- [x] 一些总结
+## 串
+
+- 基本概念
+- 简单的模式匹配
+- KMP算法
+
+## 树与二叉树
+
+- 基本概念
+- 二叉树
+- 二叉树遍历
+- 线索二叉树
+- 树和森林
+- 树的应用
-## 参考资料:
+## 图论
-- 《数据结构》. 严蔚敏著
-- 《数据结构-王道考研》. 2019 王道论坛
+- 基本概念
+- 图的存储
+- 图的遍历
+- 图的应用
+
+
+## 查找
+
+- 基本概念
+- 顺序查找
+- 折半查找
+- B树和B+树
+- 散列表
+
+## 排序
+
+- 基本概念
+- 插入排序
+- 交换排序
+- 选择排序
+- 归并排序
+- 基数排序
+- 外部排序
+
+
+
+
+## 参考资料
+
+- 《数据结构》严蔚敏著.
+- 《数据结构-王道考研》2019 王道论坛.
diff --git a/docs/manuscripts/ds/栈和队列/1.栈的基本概念和基本操作.md b/docs/manuscripts/ds/栈和队列/1.栈的基本概念和基本操作.md
index 820c30c..ccf9a3f 100644
--- a/docs/manuscripts/ds/栈和队列/1.栈的基本概念和基本操作.md
+++ b/docs/manuscripts/ds/栈和队列/1.栈的基本概念和基本操作.md
@@ -1,5 +1,10 @@
+---
+permalink: /test
+---
# 基本概念和基本操作
+## 基本概念
+
`栈`: 只允许在一端进行插入或者删除操作的**线性表**,`后进先出的线性表`。
@@ -36,7 +41,7 @@
可以得出结论:栈是后进先出(先进后出),即:LIFO(Last In First Out),也可以叫**后进先出的线性表**
-## 栈的基本操作
+## 基本操作
- `InitStack(&S)`: 初始化一个空栈`S`,栈顶指针初始化为-1
diff --git a/docs/manuscripts/ds/栈和队列/2.栈的顺序存储结构.md b/docs/manuscripts/ds/栈和队列/2.栈的顺序存储结构.md
index d399d64..0535312 100644
--- a/docs/manuscripts/ds/栈和队列/2.栈的顺序存储结构.md
+++ b/docs/manuscripts/ds/栈和队列/2.栈的顺序存储结构.md
@@ -7,7 +7,7 @@
> 指针指向栈顶(top)主要是因为栈在线性表的一端操作,操作的那端就是栈顶,方便操作。
-### 顺序栈的存储类型
+## 顺序栈的存储类型
```cpp
// 定义栈中元素的最大个数
@@ -42,7 +42,7 @@ typedef struct{
-### 顺序栈的基本运算
+## 顺序栈的基本运算
- `InitStack(&S)`: 初始化一个空栈`S`,栈顶指针初始化为-1
@@ -53,7 +53,7 @@ typedef struct{
- `ClearStack(&S)`: 销毁栈,释放栈`S`占用的存储空间。
-#### 初始化
+### 初始化
`InitStack(&S)`: 初始化一个空栈`S`,栈顶指针初始化为-1
@@ -68,7 +68,7 @@ void InitStack(&S){
```
-#### 栈空判断
+### 栈空判断
`StackEmpty(S)`: 判断一个栈是否为空,即:栈顶指针是否为-1,如果栈空则返回`true`,否则返回`false`
@@ -87,7 +87,7 @@ bool StackEmpty(S){
```
-#### 进栈
+### 进栈
`Push(&S,x)`: 进栈,若栈未满,`x`进栈操作,插入到栈内成为`新的栈顶元素`。
@@ -113,7 +113,7 @@ bool Push(SqStack &S,ElemType x){
- 进栈先移动栈顶指针+1,再操作入栈元素
- `++i`是简写,先对变量i进行递加操作,再进行使用,即:先加后用
-#### 出栈
+### 出栈
`Pop(&S,&x)`: 出栈,若栈非空,出栈操作,**弹出栈顶元素**,用指针`x`进行返回。
@@ -141,7 +141,7 @@ bool Pop(SqStack &S,ElemType &x){
-#### 读(获取)栈顶元素
+### 读(获取)栈顶元素
`GetTop(S,&x)`: 读栈顶元素,若栈`S`非空,用x返回栈顶元素。
@@ -178,7 +178,7 @@ bool GetTop(SqStack S,ElemType &x){
-### 共享栈
+## 共享栈
`共享栈`:利用栈底位置相对不变的特性,可以让两个顺序栈共享一个`一维存储空间`,将两个栈的栈底分别设置在共享空间的两端,两个栈顶则向共享空间的中间延伸
@@ -196,7 +196,7 @@ bool GetTop(SqStack S,ElemType &x){
当且仅当两个栈的栈顶指针相邻(`top1-top0=1`),可以判断共享栈栈满
-#### 进栈
+### 进栈
> 进栈:先移动指针,后进行赋值
@@ -205,7 +205,7 @@ bool GetTop(SqStack S,ElemType &x){
- 当0号栈进栈时,0号栈栈顶指针top0`先加1后赋值`
- 当1号栈进栈时,0号栈栈顶指针top1`先减1后赋值`
-#### 出栈
+### 出栈
> 出栈:先进行赋值,后移动指针
diff --git a/docs/manuscripts/ds/栈和队列/3.栈的链式存储结构.md b/docs/manuscripts/ds/栈和队列/3.栈的链式存储结构.md
index 8604f11..c4bed53 100644
--- a/docs/manuscripts/ds/栈和队列/3.栈的链式存储结构.md
+++ b/docs/manuscripts/ds/栈和队列/3.栈的链式存储结构.md
@@ -1,4 +1,3 @@
-
# 栈的链式存储结构
`链栈`: 采用链式存储的栈
@@ -43,14 +42,14 @@ typedef struct LinkStack
```
-### 优点
+优点:
- 便于多个栈共享存储空间
- 不存在栈满上溢的情况,避免程序因溢出导致出错
- 有效的提高存取效率
-### 进栈
+## 进栈
- 如果链栈不存在,则栈满,入栈操作失败,返回false;
- 如果链栈存在,进行单链表的结点插入操作,移动指针,结点元素赋值,再将结点压入链栈中,移动链栈栈顶指针,最后链栈元素总数+1,返回true
@@ -88,7 +87,7 @@ bool linkStackPushNode(LinkStack* linkStack,int e){
}
```
-### 出栈
+## 出栈
- 如果链栈不存在,或者为空栈,则无法进行出栈操作,返回false
- 如果链栈满足出栈条件,则通过栈顶指针获取到链栈栈底结点,将其数据域赋值给变量e,移动栈顶指针指向待出栈元素的后继结点,同时释放待出栈元素的内存空间,链栈元素总数-1 ,出栈成功,返回true.
diff --git a/docs/manuscripts/ds/栈和队列/4.队列的基本概念和操作.md b/docs/manuscripts/ds/栈和队列/4.队列的基本概念和操作.md
index 72c953c..61f9b6c 100644
--- a/docs/manuscripts/ds/栈和队列/4.队列的基本概念和操作.md
+++ b/docs/manuscripts/ds/栈和队列/4.队列的基本概念和操作.md
@@ -1,7 +1,7 @@
# 队列的基本概念和操作
-### 基本概念
+## 基本概念
`队列`:和栈一样,是一种操作受限制的线性表,只允许在表的一端进行插入,在表的另外一端进行删除,简称为`队`,常记作:`Queue`
@@ -29,7 +29,7 @@
-### 基础操作
+## 基础操作
> 和栈一样,队列是操作受限的线性表,具有`先进先出`的特性,不是任何对线性表的操作都可以作为队列的操作。例如:无法随便读取到队列中间的某个数据,需要将前面的元素先出队
diff --git a/docs/manuscripts/ds/栈和队列/5.队列的顺序存储结构.md b/docs/manuscripts/ds/栈和队列/5.队列的顺序存储结构.md
index 10f31fe..abcdae7 100644
--- a/docs/manuscripts/ds/栈和队列/5.队列的顺序存储结构.md
+++ b/docs/manuscripts/ds/栈和队列/5.队列的顺序存储结构.md
@@ -75,7 +75,7 @@ typedef struct {
-### 循环队列
+## 循环队列
在上面的顺序队列中,当队满后进行出队列,由于顺序队列出队只在队首进行操作,并且只会修改队首指针,这时候就会出现队尾指针一直`Q.rear===MaxSize`情况,就如下:
@@ -124,10 +124,10 @@ typedef struct {
-#### 如何区别队空还是队满
+### 如何区别队空还是队满
> 为了很好的区别循环队列的`队空`还是`队满`的情况,一般有三种处理方式.
-##### 牺牲一个单元来区分队空和队满
+#### 牺牲一个单元来区分队空和队满
这种方式**要求在入队时少用一个队列单元**,是一种比较普遍的做法。约定:
@@ -138,7 +138,7 @@ typedef struct {
- 队列中元素个数:`(Q.rear+MaxSize-Q.front)%MaxSize`
-##### 类型中增设表示元素个数的数据成员
+#### 类型中增设表示元素个数的数据成员
这种就很直接了,直接和MaxSize去比较,就可以有:
@@ -148,7 +148,7 @@ typedef struct {
值的注意的是:在这个前提下,不论是`队空`还是`队满`,对会存在`Q.front=Q.rear`,这个可以通过前面方案解决。
-##### 类型中增设tag数据成员标记
+#### 类型中增设tag数据成员标记
通过添加tag标记的方式,区分`队空`还是`队满`
@@ -194,13 +194,13 @@ int DeLoopQueue(SqQueue &Q, ElemType &x){
}
```
-#####
+####
-### 代码实现
+## 代码实现
-#### 初始化空队列
+### 初始化空队列
```cpp
@@ -217,7 +217,7 @@ void InitLoopQueque(&Q){
}
```
-#### 队列是否为空
+### 队列是否为空
```cpp
/*
@@ -242,7 +242,7 @@ bool isEmpatyLoopQueue(Q){
```
-#### 入队操作
+### 入队操作
```cpp
/*
@@ -273,7 +273,7 @@ bool EnLoopQueue(SqQueue &Q, ElemType x){
```
-#### 出队操作
+### 出队操作
```cpp
@@ -309,8 +309,7 @@ bool DeLoopQueue(SqQueue &Q, ElemType &x){
```
-#### 获取队头元素
-
+### 获取队头元素
```cpp
/*
* @Description: 获取循环队列队头元素
diff --git a/docs/manuscripts/ds/栈和队列/6.队列的链式存储结构.md b/docs/manuscripts/ds/栈和队列/6.队列的链式存储结构.md
index 05ca7cb..660cb22 100644
--- a/docs/manuscripts/ds/栈和队列/6.队列的链式存储结构.md
+++ b/docs/manuscripts/ds/栈和队列/6.队列的链式存储结构.md
@@ -59,11 +59,11 @@ typedef struct{
- 不存在队满情况,也不会出现溢出情况;
- 链式队列不会出现存储分配不合理、“溢出”的情况,内存动态分配
-### 基本操作
+## 基本操作
> Tips: 基于带头结点链式队列的基础操作
-#### 队列初始化
+### 队列初始化
```cpp
@@ -89,7 +89,7 @@ voide InitLinkQueue(LinkQueue &Q){
> 链式队列,本质是基于单链表的队列,那带头结点的链式队列,其实强调的也就是单链表要带头结点。队列的队首指针和队尾指针确定指向单表的队首和队尾就ok;初始化的时候,带头结点的单链表实质就只有一个头结点。而此时的链式队列,需要将队首指针和队尾指针指向单链表的头结点就行,好像到这里就完了。但是这样却忽视了单链表,只是注重的队列的front和rear指针。单链表的结点元素是分数据域和指针域的,即便是头结点的数据域可以不存什么(当然也常会存一些链表信息什么的),此处的`Q.front->next=NULL`就是用来处理链表的头结点的指针域的,让其指向下一个单链表元素为空,这里是非常需要处理的,非常细节!
-#### 判断队空
+### 判断队空
```cpp
@@ -112,7 +112,7 @@ bool IsEmptyLinkQueue(LinkQueue Q){
}
```
-#### 入队
+### 入队
```cpp
@@ -143,7 +143,7 @@ void EnLinkQueue(LinkQueue &Q, ElemType x){
```
-#### 出队
+### 出队
```cpp
@@ -193,7 +193,7 @@ bool DeLinkQueue(LinkQueue &Q, ElemType &x){
-### 双端队列
+## 双端队列
`双端队列`: 允许在两端都可以进行入队和出队操作的队列,元素的逻辑结构仍然是线性结构
@@ -216,7 +216,7 @@ bool DeLinkQueue(LinkQueue &Q, ElemType &x){
-### 队列的补充
+## 知识补充
- 最适合用来链队的链表是:`带队首指针和队尾指针的非循环单链表`
diff --git a/docs/manuscripts/ds/栈和队列/7.总结.md b/docs/manuscripts/ds/栈和队列/7.栈VS队列补充.md
similarity index 99%
rename from docs/manuscripts/ds/栈和队列/7.总结.md
rename to docs/manuscripts/ds/栈和队列/7.栈VS队列补充.md
index b9b98b6..eac06bd 100644
--- a/docs/manuscripts/ds/栈和队列/7.总结.md
+++ b/docs/manuscripts/ds/栈和队列/7.栈VS队列补充.md
@@ -1,11 +1,11 @@
-# 栈和队列总结
+# 栈VS队列补充
> Tips: 这里不会做过多文字介绍相关应用,具体需要自己看书、查资料揣摩
```mindmap
-root(栈和队列总结)
+root(栈VS队列补充)
栈的应用
队列的应用
数组的定义
diff --git a/docs/manuscripts/os/readme.md b/docs/manuscripts/os/readme.md
index 18af380..80109d6 100644
--- a/docs/manuscripts/os/readme.md
+++ b/docs/manuscripts/os/readme.md
@@ -1,20 +1,16 @@
# 操作系统
-
-
```mindmap
操作系统
系统概述
-
进程管理
-
进程管理
-
文件管理
-
- I/O管理
+ 输入、输出管理
```
+
+
## 系统概述
- 引论
@@ -24,16 +20,37 @@
## 进程管理
-- 进程基本概念和特征
-- 进程的运行
+
+- 进程和线程
+- 处理机调度
+- 进程同步
+- 死锁
+
## 内存管理
+- 引论
+- 虚拟内存
+- 一些总结
+
## 文件管理
-## I/O管理
+- 基本概念
+- 文件系统
+- 磁盘管理
+- 一些总结
-## 参考资料:
-- 《计算机操作系统-第四版》. 汤小丹、梁红兵、汤子瀛著
-- 《操作系统-王道考研》. 2019 王道论坛
+## 输入、输出管理
+
+- 基本概述
+- 核心子系统
+- 高速缓存和缓冲区
+- 一些总结
+
+
+
+## 参考资料
+
+- 《计算机操作系统》 第四版 汤小丹、梁红兵、汤子瀛著
+- 《操作系统-王道考研》 2019 王道论坛
diff --git a/manuscripts/数据结构/基础入门/1.基础概念.md b/manuscripts/数据结构/基础入门/1.基础概念.md
deleted file mode 100644
index d85a517..0000000
--- a/manuscripts/数据结构/基础入门/1.基础概念.md
+++ /dev/null
@@ -1,71 +0,0 @@
-# 基础概念
-
-### 数据
-
-**信息的载体**,是客观事物属性的数、字符以及所有能够输入到计算机包中并且被计算机程序识别和处理的**集合**
-
-### 数据元素
-
-**数据的基本单位**,通常按照一个整数来进行考虑和处理。
-
-
-特别注意:一个数据元素由若干个**数据项**组成,数据项是构成数组元素的最小单位,且不可分割。
-
-
-
-### 数据对象
-
-具有**相同性质**的数据元素的**集合**,**是数据的子集**
-
-
-### 数据类型
-
-值的集合和定义在此集合上一组操作的总称
-
-- 原子类型:不可再分的数据类型;
-- 结构类型:可以分解成若干分量(成分)的数据类型;
-- **抽象数据类型**:抽象出具组织和其相关的操作;
-
-
-
-### 抽象数据类型(ADT)
-
-> Tips: 可以结合高级语言中类对象封装来理解;
-
-```bash
-ADT抽象数据类型名{
- 数据对象:<数据对象的定义>
- 数据关系:<数据关系的定义>
- 基本操作:<基本操作的定义>
-} ADT抽象数据类型名
-```
-
-
-
-一个数学模型以及定义在该模型上的一组操作。定义仅仅取决于它的一组逻辑操作。与计算机内部如何表示和实现是没有关系;
-
-**不论内部结构如何变化,只要其数学特性不变,就不会影响到外部的使用,实现了数据封装和信息隐藏**
-
-
-通常由(数据对象、数据关系、数据操作集)三元组来表示抽象数据类型;
-
-抽象数据类型的主要作用是**数据封装和信息隐藏,让实现与使用相分离**。数据及其相关操作的结合称为数据封装。对象可以对其他对象隐藏某些操作细节,从而使这些操作不会受到其他对象的影响。
-
-抽象数据类型独立于运算的具体实现,使用户程序只能通过抽象数据类型定义的某些操作来访问其中的数据,实现了信息隐藏。
-
-
-### 数据结构
-
-
-首先明确:数据元素都不是孤立存在的。元素与元素之间存在着某种关系,这种相互之间的关系就是**结构**。
-
-**数据结构是相互之间存在一种或者多种特定关系的数据元素的集合**
-
-- 逻辑结构
-- 存储结构(物理结构)
-- 数据运算
-
-数据的逻辑结构和存储结构是密不可分的。
-
-**算法的设计取决于所选定的逻辑结构;算法的实现依赖于所采用的存储结构;**
-
diff --git a/manuscripts/数据结构/基础入门/2.数据结构三要素.md b/manuscripts/数据结构/基础入门/2.数据结构三要素.md
deleted file mode 100644
index 91dbe45..0000000
--- a/manuscripts/数据结构/基础入门/2.数据结构三要素.md
+++ /dev/null
@@ -1,133 +0,0 @@
-# 数据结构三要素
-
-- 数据的逻辑结构
-- 数据的存储结构
-- 数据的运算
-
-
-### 数据的逻辑结构
-
-数据元素之间的逻辑关系,从逻辑关系上描述数据,叫做数据的逻辑结构。
-
-与数据的存储(物理)结构无关,是独立于计算机的。
-
-可以分为:
-
-- 线性结构
-- 非线性结构
-
-
-线性表是典型的线性结构,衍生出的栈、队列、串、数组、广义表也都是线性结构;
-
-非线性结构主要有:集合、树(一般树、二叉树)、图(有向图、无向图)
-
-
-特别注意:
-
-- `集合`:结构中的数据元素之间**除了“同属于一个集合”的关系外,别无其他关系。**
-- `线性结构`:结构中的数据元素之间**只存在一对一的关系**。
-- `树形结构`:结构中的数据元素之间**存在一对多的关系。**
-- `图状结构和网状结构`:结构中的数据元素之间**存在多对多的关系。**
-
-
-
-### 数据的存储(物理)结构
-
-数据结构在计算机中的表示(映像)。包括数据`元素的表示`和`关系的表示`。
-
-存储结构是逻辑结构用计算机语言实现的,依赖于计算机语言。
-
-
-可以分为:
-
-- 顺序存储
-- 链式存储
-- 索引存储
-- 散列(Hash)存储
-
-**注意:存储和存取的概念不一样**
-
-#### 顺序存储
-
-**逻辑上相邻的元素存储在物理位置上也相邻的存储单元里,元素之间的关系由存储单元的邻接关系来体现。**
-
-优点:
-
-- 可以实现随机存取
-- 元素占用最少的存储空间
-
-缺点:
-
-- 只能使用相邻的一整块存储单元,依赖于物理结构相邻;
-- 容易产生`外部碎片`
-
-什么是内外部碎片?
-
-> 参考资料:https://blog.csdn.net/qq_22238021/article/details/80209062
-
-- 外部碎片:`还没有分配出去`(不属于任何进程),但是**由于大小而无法分配给申请内存空间的新进程的内存空闲块。**
-- 内部碎片:`已经被分配出去`(能明确指出属于哪个进程)的**内存空间大于请求所需的内存空间,不能被利用的内存空间就是内部碎片。**
-
-
-#### 链式存储
-
-与顺序存储不同,**链式存储不要求逻辑上相邻的元素在物理位置上也相邻。**
-
-借助指示元素存储地址的`指针`表示元素之间的逻辑关系。
-
-
-优点:
-
-- 不会出现碎片现象
-- 充分利用所有存储单元
-
-缺点:
-
-- 除了存储元素外,还需要额外存储指针,会占用额外的存储空间(结合数据库索引学习)。
-- 链式存储,**只能实现`顺序存取`,不能实现`随机存取`(指针的遍历)**
-
-
-#### 索引存储
-
-存放数据元素和元素间关系的存储方式,在存储元素信息的同时,还需要建立附加的`索引表`。
-
-**索引表的每一项称为索引项,索引项的一般形式是:<关键字,地址>**
-
-优点:
-
-- 检索快(就好比字典有了目录,查询就很快了)
-
-
-缺点:
-
-- 增加了索引表,占用较多的存储空间(典型的空间换时间策略)
-- 增加、删除数据时,需要对应修改索引表,花费更多时间。
-
-#### 散列(Hash)存储
-
-根据元素的关键字直接通过散列(Hash)函数计算出元素的存储地址。
-
-
-优点:
-
-- 检索快,添加、删除元素结点操作快(获取元素地址直接,整体时间就少了)
-
-
-缺点:
-
-- 非常依赖于`散列函数`
-- 会出现`散列冲突`(主要依赖与散列函数,散列函数不好就很容易出现散列冲突)
-- 出现`散列冲突`时,解决冲突就会增加时间和空间上的开销
-
-
-### 数据的运算
-
-数据上的运算包括:`运算的定义` 、`运算的实现`
-
-- `运算的定义`:针对逻辑结构,指出运算的功能
-- `原酸的实现`:针对存储结构,指出运算的具体操作步骤
-
-
-
-
-线性表既可以用顺序存储方式实现,也可以用链式存储方式实现。
\ No newline at end of file
diff --git a/manuscripts/数据结构/基础入门/3.算法和算法评价.md b/manuscripts/数据结构/基础入门/3.算法和算法评价.md
deleted file mode 100644
index e0be47f..0000000
--- a/manuscripts/数据结构/基础入门/3.算法和算法评价.md
+++ /dev/null
@@ -1,102 +0,0 @@
-# 算法和算法评价
-
-
-### 算法
-
-`算法` : 对特定问题求解步骤的一种描述,**是指令的有序集合**,每一条指令表示一个或多个操作。
-
-
-#### 重要特性
-
-- `有穷性`:必须总是(对任何合法的输入值)在**执行有穷步后结束**,并且每一步都可**在有穷时间内完成**
-- `确定性`:每条指令的含义明确,不会产生二义性(歧义),**对相同的输入只能得出相同的结果**
-- `可行性`:算法是可行的。**算法中描述的操作都是可以通过已经实现的基本运算执行有限次来实现的**
-- `输入`:有零个或者多个输入,**输入取决于某个特定的对象的集合。**
-- `输出`:有一个或者多个输出,**输出是和输入有着某种特定关系的量(强调输出与输入的关系)**
-
-
-> **算法是有穷的,但是程序不一定满足有穷性**,程序只是算法在计算机上的特定的实现, 例如:死循环
-
-#### 算法的目标
-
-由于设计思路、解决问题方案等方面不同,不同算法之间也是有好坏的,就像人与人之间存在着差异。为设计出更好的算算法,往往需要追求更高的目标,而好的算法需要考虑到的目标就有:
-
-- 正确性:首先算法肯定是**需要正确的解决求解问题**
-- 可读性:**算法应该具有良好的可读性**,就像项目代码一样,好的业务代码、逻辑清楚,**便于理解**。
-- 健壮性:**在输入非法数据时,算法也能适当地做出反应或进行处理,而不会产生莫名奇妙的输出结果**(在高级语言编程中,类似于强调封装方法的参数校验)
-- 效率与低存储量需求:**效率即算法执行的时间**,**存储量需求即算法那执行过程中所有要的最大存储空间**,这些与算法所解决问题的规模有关;
-
-
-> 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)
\ No newline at end of file
diff --git a/manuscripts/数据结构/基础入门/readme.md b/manuscripts/数据结构/基础入门/readme.md
deleted file mode 100644
index 7c0680c..0000000
--- a/manuscripts/数据结构/基础入门/readme.md
+++ /dev/null
@@ -1,10 +0,0 @@
-## 基础入门
-
-
-### 主要内容
-
-- [基础概念](1.基础概念.md)
-- [数据结构三要素](2.数据结构三要素.md)
-- [算法与算法评价](3.算法和算法评价.md)
-
-
\ No newline at end of file
diff --git a/manuscripts/数据结构/基础入门/基础入门_水印.jpg b/manuscripts/数据结构/基础入门/基础入门_水印.jpg
deleted file mode 100644
index bfdd3be..0000000
Binary files a/manuscripts/数据结构/基础入门/基础入门_水印.jpg and /dev/null differ
diff --git a/manuscripts/数据结构/思维导图/基础入门.xmind b/manuscripts/数据结构/思维导图/基础入门.xmind
deleted file mode 100644
index 8676310..0000000
Binary files a/manuscripts/数据结构/思维导图/基础入门.xmind and /dev/null differ
diff --git a/manuscripts/数据结构/思维导图/栈和队列.xmind b/manuscripts/数据结构/思维导图/栈和队列.xmind
deleted file mode 100644
index 0b47050..0000000
Binary files a/manuscripts/数据结构/思维导图/栈和队列.xmind and /dev/null differ
diff --git a/manuscripts/数据结构/思维导图/线性表.xmind b/manuscripts/数据结构/思维导图/线性表.xmind
deleted file mode 100644
index a1d5818..0000000
Binary files a/manuscripts/数据结构/思维导图/线性表.xmind and /dev/null differ
diff --git a/manuscripts/数据结构/数据结构和算法.md b/manuscripts/数据结构/数据结构和算法.md
deleted file mode 100644
index 0789986..0000000
--- a/manuscripts/数据结构/数据结构和算法.md
+++ /dev/null
@@ -1,1599 +0,0 @@
-# 数据结构和算法
-
-## 基础入门
-
-### 基本概念
-
-- 数据
-
- - 信息的载体
- - 客观事物属性的数、字符以及所有能够输入到计算机包中并且被计算机程序识别和处理的集合
-
-- 数据元素
-
- - 数据的基本单位
- - 一个数据元素由若干个数据项组成
- - 数据项是构成数组元素的最小单位,且不可分割
-
-- 数据对象
-
- - 具有相同性质的数据元素的集合
- - 数据的子集
-
-- 数据类型
-
- - 原子类型:不可再分的数据类型
- - 结构类型:可以分解成若干分量(成分)的数据类型
- - 抽象数据类型:抽象出具组织和其相关的操作
-
-- 抽象数据类型(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/manuscripts/数据结构/栈和队列/1.栈的基本概念和基本操作.md b/manuscripts/数据结构/栈和队列/1.栈的基本概念和基本操作.md
deleted file mode 100644
index 9a5567a..0000000
--- a/manuscripts/数据结构/栈和队列/1.栈的基本概念和基本操作.md
+++ /dev/null
@@ -1,51 +0,0 @@
-## 基本概念和基本操作
-
-`栈`: 只允许在一端进行插入或者删除操作的**线性表**,`后进先出的线性表`。
-
-
-- 明确栈是一种线性表
-- 限定栈只能在某一端进行插入或者删除操作
-
-
-
-`栈顶`:线性表允许进行插入和删除的一端。
-
-`栈底`:不允许进行插入和删除的另外一端,是**固定的**。类似杯底这中概念
-
-`空栈`:不含任何元素的空表,也叫**栈空**
-
-
-
-基本结构如下:
-
-
-
-
-在上面的基本结构中,可以假设存在栈S=(a1,a2,a3,a4,a5,a6,a7,a8),很明显
-
-- 栈顶元素:a1
-- 栈底元素:a8
-
-
-栈只能在栈顶进行插入和删除操作
-
-- 进栈顺序:a1->a2->a3->a4->a5->a6->a7->a8
-- 出栈顺序:a8->a7->a6->a5->a4->a3->a2->a1
-
-
-可以得出结论:栈是后进先出(先进后出),即:LIFO(Last In First Out),也可以叫**后进先出的线性表**
-
-
-## 栈的基本操作
-
-
-- `InitStack(&S)`: 初始化一个空栈`S`,栈顶指针初始化为-1
-- `StackEmpty(S)`: 判断一个栈是否为空,如果栈空则返回`true`,否则返回`false`
-- `Push(&S,x)`: 进栈,若栈未满,`x`进栈操作,插入到栈内成为`新的栈顶元素`。
-- `Pop(&S,&x)`: 出栈,若栈非空,出栈操作,**弹出栈顶元素**,用指针`x`进行返回。
-- `GetTop(S,&x)`: 读栈顶元素,若栈`S`非空,用x返回栈顶元素。
-- `ClearStack(&S)`: 销毁栈,释放栈`S`占用的存储空间。
-
-
-
-> Tips: `&`是C++特有的,可以用来表示引用调用,类似`传址目的`,可以类比指针。 当然,在C语言中*代表指针,指向存储地址,也是具有`传址目的`
\ No newline at end of file
diff --git a/manuscripts/数据结构/栈和队列/2.栈的顺序存储结构.md b/manuscripts/数据结构/栈和队列/2.栈的顺序存储结构.md
deleted file mode 100644
index 6f17dc8..0000000
--- a/manuscripts/数据结构/栈和队列/2.栈的顺序存储结构.md
+++ /dev/null
@@ -1,221 +0,0 @@
-
-## 栈的顺序存储结构
-
-
-`顺序栈`:栈的顺序存储,利用一组地址连续的存储单元存放自栈底到栈顶的所有元素,同时**附加一个用来指向当前栈顶位置的指针**
-
-> 指针指向栈顶(top)主要是因为栈在线性表的一端操作,操作的那端就是栈顶,方便操作。
-
-
-### 顺序栈的存储类型
-
-```cpp
-// 定义栈中元素的最大个数
-# define MaxSize 50
-
-// 结构体定义
-typedef struct{
- ElemType data[MaxSize]; // 存放栈中元素
- int top; // 栈顶指针
-}SqStack;
-
-```
-
-假设存在顺序栈S,则:
-
-- 栈顶指针:S.top 初始化时设置S.top=-1
-- 栈顶元素:S.data[S.top],其中S.top为栈顶指针
-- 进栈操作:栈不满时,栈顶指针+1,再送值到栈顶元素
-- 出栈操作:栈非空时,先取栈顶元素值,再将栈顶指针-1
-- 栈空条件:S.top=-1
-- 栈满条件:S.top=MaxSize-1
-- 栈长:S.top+1
-
-
-> Tips: 进栈先移动指针,避免占满,元素无法添加,出现外溢;出栈先取栈顶元素,保证指针永远指向栈顶。
-
-
-
-顺序栈的存储结构体定义可以很明显看出,顺序栈的入栈操作会受到数组上界(MaxSize)的约束。
-
-**当对栈的最大使用空间估计不足时,容易出现栈上溢(外溢),需要主动向用户报告反馈,避免出现错误;**
-
-
-
-### 顺序栈的基本运算
-
-
-- `InitStack(&S)`: 初始化一个空栈`S`,栈顶指针初始化为-1
-- `StackEmpty(S)`: 判断一个栈是否为空,如果栈空则返回`true`,否则返回`false`
-- `Push(&S,x)`: 进栈,若栈未满,`x`进栈操作,插入到栈内成为`新的栈顶元素`。
-- `Pop(&S,&x)`: 出栈,若栈非空,出栈操作,**弹出栈顶元素**,用指针`x`进行返回。
-- `GetTop(S,&x)`: 读栈顶元素,若栈`S`非空,用x返回栈顶元素。
-- `ClearStack(&S)`: 销毁栈,释放栈`S`占用的存储空间。
-
-
-#### 初始化
-
-
-`InitStack(&S)`: 初始化一个空栈`S`,栈顶指针初始化为-1
-
-```cpp
-
-void InitStack(&S){
- // 栈顶指针-1
- s.top=-1;
-}
-
-```
-
-
-#### 栈空判断
-
-`StackEmpty(S)`: 判断一个栈是否为空,即:栈顶指针是否为-1,如果栈空则返回`true`,否则返回`false`
-
-```C++
-
-bool StackEmpty(S){
- if(S.top==-1){
- // 栈空
- return true;
- }else{
- // 栈非空
- return false;
- }
-}
-
-```
-
-
-#### 进栈
-
-
-`Push(&S,x)`: 进栈,若栈未满,`x`进栈操作,插入到栈内成为`新的栈顶元素`。
-
-```C++
-
-bool Push(SqStack &S,ElemType x){
- if(S.top==MaxSize-1){
- // 栈满,返回false,元素无法进行进栈操作
- return false;
- }else{
- // 可进栈,栈顶指针+1,再元素入栈
- S.data[++S.top]=x;
-
- // 入栈成功
- return true;
- }
-}
-
-```
-注意:
-
-- 进栈先移动栈顶指针+1,再操作入栈元素
-- `++i`是简写,先对变量i进行递加操作,再进行使用,即:先加后用
-
-#### 出栈
-
-`Pop(&S,&x)`: 出栈,若栈非空,出栈操作,**弹出栈顶元素**,用指针`x`进行返回。
-
-```C++
-
-bool Pop(SqStack &S,ElemType &x){
- if(S.top==-1){
- // 栈空,无栈顶元素可出栈,返回false
- return false;
- }else{
- // 栈非空,先元素出栈,再进行指针-1
- x=S.data[S.top--];
-
- // 出栈成功,返回true
- return true;
- }
-}
-
-```
-
-注意:
-
-- 出栈操作,先让元素出栈,获取栈顶元素,再移动指针-1
-- `i--`是先使用变量i,再对变量做递减操作,即:先用后加(减)
-
-
-
-#### 读(获取)栈顶元素
-
-`GetTop(S,&x)`: 读栈顶元素,若栈`S`非空,用x返回栈顶元素。
-
-```C++
-
-bool GetTop(SqStack S,ElemType &x){
-
- if(S.top==-1){
- // 栈空,无栈顶元素,返回false
- return false;
- }else{
-
- // 通过栈顶指针,获取栈顶元素,赋值给变量x
- x=S.data[S.top];
-
- // 读取栈顶元素成功,返回true
- return true;
- }
-}
-
-```
-
-
-
-**上面的这些操作都是基于栈顶指针初始化为`-1`的情况**
-
-当栈顶指针初始化为`S.top=0`,相关操作操作会有区别:
-
-- 入栈: `S.data[S.top++]=x`
-- 出栈: `x=S.data[--S.top]`
-
-
-**同时, 栈空、栈满条件也会有变化,要仔细对比揣摩**
-
-
-
-### 共享栈
-
-
-`共享栈`:利用栈底位置相对不变的特性,可以让两个顺序栈共享一个`一维存储空间`,将两个栈的栈底分别设置在共享空间的两端,两个栈顶则向共享空间的中间延伸
-
->Tips: 类似头对头,一致对外这种感觉,噗呲哈哈
-
-
-
-
-在上面的共享栈结构图中,两个栈(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),在栈顶操作。
-
-
-**共享栈对存取效率没有什么影响**
\ No newline at end of file
diff --git a/manuscripts/数据结构/栈和队列/3.栈的链式存储结构.md b/manuscripts/数据结构/栈和队列/3.栈的链式存储结构.md
deleted file mode 100644
index c876cb9..0000000
--- a/manuscripts/数据结构/栈和队列/3.栈的链式存储结构.md
+++ /dev/null
@@ -1,131 +0,0 @@
-
-## 栈的链式存储结构
-
-`链栈`: 采用链式存储的栈
-
-`栈满`:对于链栈来说,是基于链式存储的,基本不存在栈满的情况,除非内存已经没有使用空间了。
-
-`栈空`:对于空栈来说,链表原来的定义是头指针指向空,那么链栈的空其实就是`top=NULL`,链栈元素总数为0
-
-
-栈只是栈顶在做插入和删除操作,栈顶应该放在单链表的头部,所以单链表中的头结点也就失去了意义。
-
-**通常对于链栈来说,是不需要头结点的,当然也存在带头结点的链栈**
-
-
-
-
-
-栈的链式存储类型:
-
-```cpp
-
-// 链栈类型定义【基础】
-typedef struct LinkNode{
- ElemType data; // 栈元素结点数据域
- struct LinkNode *next; // 栈元素结点指针域
-} *LinkStack;
-
-// 更为详细的定义
-
-typedef struct StackNode
-{
- int data;//结点数据域
- struct StackNode* next;//结点指针域
-}StackNode,* Linktop;
-
-//链栈的数据结构
-typedef struct LinkStack
-{
- Linktop top; //栈顶结点,定义了一个指向上个结构体的指针
- int count;//元素个数
-}LinkStack;
-
-```
-
-### 优点
-
-- 便于多个栈共享存储空间
-- 不存在栈满上溢的情况,避免程序因溢出导致出错
-- 有效的提高存取效率
-
-
-### 进栈
-
-- 如果链栈不存在,则栈满,入栈操作失败,返回false;
-- 如果链栈存在,进行单链表的结点插入操作,移动指针,结点元素赋值,再将结点压入链栈中,移动链栈栈顶指针,最后链栈元素总数+1,返回true
-
-
-```cpp
-
-/*
- * @Description: 基于单链表链栈的进栈操作
- * @Version: Beta1.0
- * @Author: 【B站&公众号】Rong姐姐好可爱
- * @Date: 2020-03-04 07:36:04
- * @LastEditors: 【B站&公众号】Rong姐姐好可爱
- * @LastEditTime: 2020-03-04 11:39:16
- */
-bool linkStackPushNode(LinkStack* linkStack,int e){
-
- // 判断链栈是否存在
- if (!linkStack){
- //链栈不存在,无法进栈操作,返回false
- return false;
- }
- // 开辟栈结点元素内存控件
- StackNode* node = (StackNode*)malloc(sizeof(StackNode));
- // 新结点指针域指向链表,即栈顶指针位置,元素加入链表
- node->next = linkStack->top;
- // 新结点数据域赋值
- node->data = e;
- // 元素进栈,移动栈顶指针,指向新入栈的元素
- linkStack->top = node;
- // 链栈元素总数+1
- linkStack->count++;
- //链栈入栈成功,返回true
- return true;
-}
-```
-
-### 出栈
-
-- 如果链栈不存在,或者为空栈,则无法进行出栈操作,返回false
-- 如果链栈满足出栈条件,则通过栈顶指针获取到链栈栈底结点,将其数据域赋值给变量e,移动栈顶指针指向待出栈元素的后继结点,同时释放待出栈元素的内存空间,链栈元素总数-1 ,出栈成功,返回true.
-
-
-```cpp
-
-/*
- * @Description: 基于单链表链栈的出栈操作
- * @Version: Beta1.0
- * @Author: 【B站&公众号】Rong姐姐好可爱
- * @Date: 2020-03-04 23:38:04
- * @LastEditors: 【B站&公众号】Rong姐姐好可爱
- * @LastEditTime: 2020-03-04 23:39:16
- */
-bool linkStackPopNode(LinkStack* linkStack,int *e){
- // 判断链栈是否存在及是否为空
- if (!linkStack || linkStack->count==0){
- //出栈失败,返回false
- return false;
- }
- // 获取栈顶元素结点
- StackNode* node = stack->top;
-
- // 结点元素数据域赋值给变量e
- *e = linkStack->data;
- // 移动栈顶指向,栈顶指针指向待出栈结点的后继结点
- linkStack->top = node->next;
- // 变量e已被赋值,释放链栈出栈元素的内存控件
- free(node);
- // 链栈元素个数-1
- linkStack->count--;
- // 出栈成功,返回true.
- return true;
-}
-
-```
-
-
-以上是基于单链表的链栈入栈、出栈操作,很明显**时间复杂度都为O(1)**,重点`注意移动指针,保持不断链`
\ No newline at end of file
diff --git a/manuscripts/数据结构/栈和队列/4.队列的基本概念和基础操作.md b/manuscripts/数据结构/栈和队列/4.队列的基本概念和基础操作.md
deleted file mode 100644
index b4e13bc..0000000
--- a/manuscripts/数据结构/栈和队列/4.队列的基本概念和基础操作.md
+++ /dev/null
@@ -1,42 +0,0 @@
-## 队列的基本概念和基础操作
-
-
-### 基本概念
-
-`队列`:和栈一样,是一种操作受限制的线性表,只允许在表的一端进行插入,在表的另外一端进行删除,简称为`队`,常记作:`Queue`
-
-`入队`: 向队列中插入元素,也叫做`进队`
-
-`出队`: 删除队列元素,也叫做`离队`
-
-
-
-
-结合生活中排队的经验,在群体素质高、无人插队的情况下(`薛定谔排队`,噗呲,哈哈哈),**一般最早排队的也是最早离队的**,和栈的`后进先出`不一样的是,队列是`先进先出`的,即:First In Frist Out
-
-> Tips
-> - 栈:又叫做后进先出的线性表
-> - 队列:又叫做先进先出的线性表
-
-
-
-`队头`:允许进行删除操作的一端,也叫做`队首`,常记作:`Front`
-
-`队尾`:允许进行插入操作的一端,常记作:`Rear`
-
-`空队列`:不含任何元素的空表,注意这个表是指`线性表`
-
-
-
-
-### 基础操作
-
-
-> 和栈一样,队列是操作受限的线性表,具有`先进先出`的特性,不是任何对线性表的操作都可以作为队列的操作。例如:无法随便读取到队列中间的某个数据,需要将前面的元素先出队
-
-
-- `InitQueue(&Q)`: 初始化一个队列,构造空队列Q
-- `QueueEmpty(Q)`: 判断队列是否为空,队空返回true,否则返回false
-- `EnEmpty(&Q,x)`: 入队,如果队列Q未满,将x入队,成为新的队尾元素
-- `DeEmpty(&Q,&x)`: 出队,如果队列Q非空,删除队头元素,复制给x返回
-- `GetHead(Q,&x)`: 读取队头元素,如果队列Q非空,则将队头元素赋值给x
diff --git a/manuscripts/数据结构/栈和队列/5.队列的顺序存储结构.md b/manuscripts/数据结构/栈和队列/5.队列的顺序存储结构.md
deleted file mode 100644
index 727b300..0000000
--- a/manuscripts/数据结构/栈和队列/5.队列的顺序存储结构.md
+++ /dev/null
@@ -1,337 +0,0 @@
-
-## 队列的顺序存储结构
-
-
-> 队列的顺序实现是指分配一块连续的存储单元用来存放队列中的元素,并且附加两个指针。
-> - `front指针`: 指向队头元素的位置
-> - `rear指针`: 指向队尾元素的位置
-
-队列顺序存储类型:
-
-```cpp
-// 队列最大存储元素个数
-#define MaxSize 50
-
-// 结构体定义
-typedef struct {
- // 存放队列元素
- ElemType data[MaxSize];
- // 队头指针和队尾指针
- int front,rear;
-} SqQueue;
-```
-
-
-假定:
-
-- 队头指针指向队头元素
-- 队尾指针指向队尾元素的下一个位置
-
-则:
-
-- 初始状态(**队空条件**):`Q.front`===`Q.rear`===0
-- 入队操作:队不满时,先赋值给队尾元素,再移动队尾指针+1
-- 出队操作: 队不空时,先取队头元素值,再移动队头指针+1
-
-
-
-
-
-在空队列中,初始状态为`Q.front===Q.rear==0`,当元素a入队时,队尾指针rear后移+1,入队成功后,`Q.front==0`、`Q.rear==1`,在队不满的情况下进队,都是`先赋值给队尾元素,再移动队尾指针rear+1`,通过上面的图宝贝可以看到,队列被元素打满的时:
-
-- 在这个进队的过程中,没有元素出队,队头指针并没有做改变,`Q.front==0`
-- 进队操作直接影响队尾指针的变化,队列满的时候`Q.rear==Maxsize`
-
-> Tips: MaxSize为队列结构体定义中,最大存储元素个数哦~
-
-
-
-
-进队说完了,那给宝贝来说说出队吧。以上图为例,队列中`Q.rear==Maxsize`、`Q.front==0`;当出现元素在队首出队,就会直接影响队首指针,从上面的流程上看:
-
-- 元素出队,front指针后移+1,在队不空的情况下,操作为:`先取队头元素值,再移动队头指针+1`
-
-- 当队列中的元素都陆续出队,抛弃了宝贝(都是渣男,噗呲,哈哈哈),指针会是:`Q.rear==Q.front==MaxSize`
-
-
-
-从上面两张图中,我们来思考:
-
-> 前面队空条件为:`Q.front===Q.rear===0`,那能用`Q.rear==MaxSize`来表示队满嘛?
-
-
-傻瓜,你在瞅瞅前面的图,明显存在`Q.rear==MaxSize`,但队列确实空的情况呀。队满要灵活判断,可不要死记书上总结的。书上说的很多结论都是有前提的,老师记结论不记前提容易张冠李戴、含糊不清的呀~
-
-
-
-
-
-
-很显然,也存在下面这种情况:
-
-- 队头指针指向队头元素的前一个位置
-- 队尾指针指向队尾元素
-
-此时的入队、出队过程就宝贝自己去画流程图咯
-
-
-
-### 循环队列
-
-在上面的顺序队列中,当队满后进行出队列,由于顺序队列出队只在队首进行操作,并且只会修改队首指针,这时候就会出现队尾指针一直`Q.rear===MaxSize`情况,就如下:
-
-
-
-
-可以很明显的看到,明明队列不满,但是由于进队列只能在队尾操作,因此不能进行进队操作;通常在这种情况下入队就会出现“上溢出”。
-
-> 需要明确的是:上溢出并不是真正的溢出,只是表明在顺序队列中队不满却无法入队的情况,是一种假的”溢出“
-
-
-
-
-这种情况在顺序队列中是非常常见的,也是顺序队列的一大缺点。为了克服这个缺点,计算机先贤们总是能够有很多很好的办法,这里不得不佩服!!,所以就有了循环队列,**一个将顺序队列臆想为一个环状的空间**
-
-
-> 很多时候就是这样,为了解决一个问题,从而衍生出一个新的知识
-
-
-`循环队列`:把顺序队列臆想为一个环状的空间,将存储队列元素的表从逻辑上看做为一个环
-
-
-
-
-
-
-当队首指针`Q.front=MaxSize-1`后,再有元素`出队`就前进一个位置自动到位置0了【注意:可以结合时钟来理解,一圈转完了】
-
-
-- 初始时:`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`
-
-
-> 是不是理解起来有点抽象,其实我最开始学到这里的时候,也不明白为什么要用`除法取余运算(%)`来实现。后来我看看了手机上的时钟指针,一圈两圈三圈的转,好像就开始悟了...其实这种取余操作在计算机知识体系中还是非常常见的,例如:组成原理中将会学到的补码,据说idea就是来源于时钟..
-
-
-**和时钟一样,顺时钟进行时间变换,在出队、入队时,队首、队尾指针都是按顺时针方向进1**
-
-
-
-
-如上图,循环队列从最开始初始化为空队列时:`Q.front==Q.rear==0`,经过元素a入队,队尾指针顺时针前移`Q.rear+1`,到元素a、b、c、d陆续入队,就好像时钟转完了一圈,循环队列已满,此时发现:`Q.front==Q.rear==0`在队满时候依然成立,所以结合前面提到的初始化对空条件:`Q.front==Q.rear==0`,用`Q.front==Q.rear`来区分`队空`和`队满`是非常不合适的。
-
-
-
-
-#### 如何区别队空还是队满
-
-> 为了很好的区别循环队列的`队空`还是`队满`的情况,一般有三种处理方式.
-##### 牺牲一个单元来区分队空和队满
-
-这种方式**要求在入队时少用一个队列单元**,是一种比较普遍的做法。约定:
-
-**队头指针在队尾指针在队尾指针的下一个位置作为队满标志【重要】**
-
-- 队满条件:`(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的主要作用:
-> - 在有元素入队的时候,设置tag=1
-> - 在有元素出队的时候,设置tag=0
-
-
-对应的算法实现:
-```cpp
-// 入队算法
-// 尾插法:Q.data[Q.rear]=x;Q.rear=(Q.rear+1)%Maxsize;Q.tag=1
-// 队空条件:Q.front== Q.rear且Q.tag==0
-int EnLoopQueue(SqQueue &Q, ElemType x){
- if(Q.front==Q.rear&&Q.tag==1){
- return 0;
- }
- Q.data[Q.rear]=x;
- Q.rear=(Q.rear+1)%MaxSize;
- Q.tag=1;
- return 1;
-}
-
-
-
-// 出队算法
-// 头结点删除:x=Q.data[Q.front];Q.front=(Q.front +1)%Maxsize;Q.tag=0
-// 队满条件:Q.front == Q.rear且Q.tag=1
-// 注意:当删除之后链表为空时,还需增加一步,将尾指针指向头结点
-int DeLoopQueue(SqQueue &Q, ElemType &x){
- if (Q.front==Q.rear&&Q.tag==0){
- return 0;
- }
- x=Q.data[Q.front];
- Q.front=(Q.front+1)%MaxSize;
- Q.tag=0;
- return 1;
-}
-
-```
-#####
-
-
-### 代码实现
-
-
-#### 初始化空队列
-
-
-```cpp
-/*
- * @Description: 循环队列初始化,队列为空
- * @Version: Beta1.0
- * @Author: 【B站&公众号】Rong姐姐好可爱
- * @Date: 2019-09-27 14:17:28
- * @LastEditors: 【B站&公众号】Rong姐姐好可爱
- * @LastEditTime: 2021-03-18 22:15:06
- */
-void InitLoopQueque(&Q){
- Q.front=Q.rear=0;
-}
-```
-
-#### 队列是否为空
-
-```C++
-/*
- * @Description: 判断循环队列是否为空
- * @Version: Beta1.0
- * @Author: 【B站&公众号】Rong姐姐好可爱
- * @Date: 2019-09-27 14:17:28
- * @LastEditors: 【B站&公众号】Rong姐姐好可爱
- * @LastEditTime: 2021-03-18 22:15:06
- */
-bool isEmpatyLoopQueue(Q){
- // 注意循环队列对空条件:Q.rear=Q.front
- if(Q.rear=Q.front){
- // 队空
- return true;
- }else{
- // 非空
- return false;
- }
-}
-
-```
-
-
-#### 入队操作
-
-```C++
-/*
- * @Description: 循环队列元素入队
- * @Version: Beta1.0
- * @Author: 【B站&公众号】Rong姐姐好可爱
- * @Date: 2019-09-27 14:17:28
- * @LastEditors: 【B站&公众号】Rong姐姐好可爱
- * @LastEditTime: 2021-03-18 22:15:06
- */
-bool EnLoopQueue(SqQueue &Q, ElemType x){
- // 判断循环队列是否已满 注意判断条件:(Q.rear+1)%MaxSize===Q.front
- if((Q.rear+1)%MaxSize===Q.front){
- // 循环队列满
- return false;
- }
- // 队列未满,可进行入队操作【队尾进行】
-
- // 队尾指针指向的数据域进行赋值
- Q.data[Q.rear]=x;
-
- //队尾指针后移+1【类似时钟的顺时针方向】
- Q.rear=((Q.rear+1)%MaxSize);
-
- // 入队成功,返回true
- return true;
-}
-
-```
-
-#### 出队操作
-
-
-```C++
-
-/*
- * @Description: 循环队列元素出队
- * @Version: Beta1.0
- * @Author: 【B站&公众号】Rong姐姐好可爱
- * @Date: 2019-09-27 14:17:28
- * @LastEditors: 【B站&公众号】Rong姐姐好可爱
- * @LastEditTime: 2021-03-18 20:32:18
- */
-bool DeLoopQueue(SqQueue &Q, ElemType &x){
-
- // 判断循环队列是否为空队列
- if(Q.rear==Q.front){
-
- // 队列为空,无法进行出队操作,返回false
- return false;
- }
-
- // 循环队列非空,元素可出队【队首操作】
-
- // 将循环队列队首指针指向的元素的数据域赋值给变量x
- x=Q.data[Q.front];
-
- // 移动队首指针,顺时针后移+1
- Q.front=(Q.front+1)%MaxSize;
-
- // 出队成功,返回true
- return true;
-}
-
-```
-
-#### 获取队头元素
-
-```C++
-/*
- * @Description: 获取循环队列队头元素
- * @Version: Beta1.0
- * @Author: 【B站&公众号】Rong姐姐好可爱
- * @Date: 2019-09-27 14:17:28
- * @LastEditors: 【B站&公众号】Rong姐姐好可爱
- * @LastEditTime: 2021-03-18 20:15:33
- */
-bool GetLoopQueueHead(SqQueue &Q, ElemType &x){
- // 判断循环队列是否为空队列
- if(Q.front==Q.rear){
- // 队列为空,没有队头元素,返回false
- return false;
- }else{
- // 获取队头指针指向元素的数据域,赋值给x
- x=Q.data[Q.front];
-
- // 获取队头元素成功,返回true
- return true;
- }
-}
-```
\ No newline at end of file
diff --git a/manuscripts/数据结构/栈和队列/6.队列的链式存储结构.md b/manuscripts/数据结构/栈和队列/6.队列的链式存储结构.md
deleted file mode 100644
index 23f2797..0000000
--- a/manuscripts/数据结构/栈和队列/6.队列的链式存储结构.md
+++ /dev/null
@@ -1,231 +0,0 @@
-
-## 队列的链式存储结构
-
-`链队列`:和顺序队列一样,基于队列的链式表示叫做`链队列`,实际上为:**一个同时带有队头指针和队尾指针的单链表**
-
-- 头指针指向队头结点
-- 尾指针指向队尾结点(单链表的最后一个结点)
-
-
-这里复习下顺序队列的定义,进行比较学习
-
-> 队列的顺序实现是指分配一块连续的存储单元用来存放队列中的元素,并且附加两个指针。
-> - 队头指针指向队头元素
-> - 队尾指针指向队尾元素的下一个位置
-
-
-
-
-队列的链式存储结构:
-
-
-```cpp
-// 链式队列节点定义
-typedef struct{
- // 结果点数据域
- ElemType data;
- // 结点指针域
- struct LinkNode *next;
-}LinkNode;
-
-// 链式队列定义
-typedef struct{
- // front 队头指针,rear 队尾指针
- LinkNode *front,*rear;
-}LinkQueue;
-
-```
-
-结合上面的`不带头结点`链式队列结构图,假设存在链式队列