mirror of
https://github.com/142vip/408CSFamily.git
synced 2026-04-05 03:27:57 +08:00
docs: 操作系统、数据结构文档排版优化
This commit is contained in:
@@ -1,5 +1,15 @@
|
||||
# 基础概念
|
||||
|
||||
```mindmap
|
||||
root(基础概念)
|
||||
数据
|
||||
数据元素
|
||||
数据对象
|
||||
数据类型
|
||||
抽象数据类型(ADT)
|
||||
数据结构
|
||||
```
|
||||
|
||||
### 数据
|
||||
|
||||
**信息的载体**,是客观事物属性的数、字符以及所有能够输入到计算机包中并且被计算机程序识别和处理的**集合**
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
|
||||
# 数据结构三要素
|
||||
|
||||
- 数据的逻辑结构
|
||||
- 数据的存储结构
|
||||
- 数据的运算
|
||||
|
||||
```mindmap
|
||||
root(数据结构三要素)
|
||||
逻辑结构
|
||||
存储(物理)结构
|
||||
顺序存储
|
||||
链式存储
|
||||
索引存储
|
||||
散列(Hash)存储
|
||||
数据的运算
|
||||
```
|
||||
|
||||
### 数据的逻辑结构
|
||||
### 逻辑结构
|
||||
|
||||
数据元素之间的逻辑关系,从逻辑关系上描述数据,叫做数据的逻辑结构。
|
||||
|
||||
@@ -32,7 +39,7 @@
|
||||
|
||||
|
||||
|
||||
### 数据的存储(物理)结构
|
||||
### 存储(物理)结构
|
||||
|
||||
数据结构在计算机中的表示(映像)。包括数据`元素的表示`和`关系的表示`。
|
||||
|
||||
|
||||
@@ -1,7 +1,19 @@
|
||||
|
||||
|
||||
# 算法和算法评价
|
||||
|
||||
```mindmap
|
||||
root(算法和算法评价)
|
||||
算法
|
||||
有穷性
|
||||
确定性
|
||||
可行性
|
||||
输入
|
||||
输出
|
||||
|
||||
算法的评价
|
||||
时间复杂度
|
||||
空间复杂度
|
||||
```
|
||||
|
||||
|
||||
### 算法
|
||||
|
||||
|
||||
@@ -28,35 +28,27 @@ export const dsSidebar = [
|
||||
link: '1.basic_concept_and_operation.md'
|
||||
},
|
||||
{
|
||||
text: '线性表的顺序表示',
|
||||
text: '顺序表示',
|
||||
link: '2.sequential_representation.md'
|
||||
},
|
||||
{
|
||||
text: '基础概念和操作',
|
||||
link: '3.chain_representation.md'
|
||||
text: '链式表示-单链表',
|
||||
link: '3.single_linked_list.md'
|
||||
},
|
||||
{
|
||||
text: '基础概念和操作',
|
||||
text: '链式表示-双链表',
|
||||
link: '4.double_linked_list.md'
|
||||
}, {
|
||||
text: '基础概念和操作',
|
||||
text: '链式表示-循环链表',
|
||||
link: '5.circular_list.md'
|
||||
},
|
||||
{
|
||||
text: '基础概念和操作',
|
||||
text: '链式表示-静态链表',
|
||||
link: '6.static_linked_list.md'
|
||||
},
|
||||
{
|
||||
text: '基础概念和操作',
|
||||
link: '7.comparison_of_sequential_list_and_linked_list.md'
|
||||
},
|
||||
{
|
||||
text: '存储结构的选取',
|
||||
link: '8.selection_of_storage_structure.md'
|
||||
},
|
||||
{
|
||||
text: '零碎知识补充',
|
||||
link: '9.piecemeal_knowledge_supplement.md'
|
||||
text: '总结',
|
||||
link: '7.总结.md'
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -66,35 +58,31 @@ export const dsSidebar = [
|
||||
collapsible: false,
|
||||
children: [
|
||||
{
|
||||
text: '栈的基本概念和基本操作',
|
||||
text: '栈-基本概念和基本操作',
|
||||
link: '1.栈的基本概念和基本操作.md'
|
||||
},
|
||||
{
|
||||
text: '栈的顺序存储结构',
|
||||
text: '栈-顺序存储结构',
|
||||
link: '2.栈的顺序存储结构.md'
|
||||
},
|
||||
{
|
||||
text: '栈的链式存储结构',
|
||||
text: '栈-链式存储结构',
|
||||
link: '3.栈的链式存储结构.md'
|
||||
},
|
||||
{
|
||||
text: '队列的基本概念和操作',
|
||||
text: '队列-基本概念和操作',
|
||||
link: '4.队列的基本概念和操作.md'
|
||||
}, {
|
||||
text: '队列的顺序存储结构',
|
||||
text: '队列-顺序存储结构',
|
||||
link: '5.队列的顺序存储结构.md'
|
||||
},
|
||||
{
|
||||
text: '队列的链式存储结构',
|
||||
text: '队列-链式存储结构',
|
||||
link: '6.队列的链式存储结构.md'
|
||||
},
|
||||
{
|
||||
text: '栈和队列的应用',
|
||||
link: '7.栈和队列的应用.md'
|
||||
},
|
||||
{
|
||||
text: '特殊矩阵的压缩存储',
|
||||
link: '8.特殊矩阵的压缩存储.md'
|
||||
text: '总结',
|
||||
link: '7.总结.md'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
|
||||
# 线性表的基础概念和基本操作
|
||||
|
||||
> 强调线性表是一种逻辑结构,不是存储结构
|
||||
|
||||
|
||||
```mindmap
|
||||
root(数据结构三要素)
|
||||
逻辑结构
|
||||
存储(物理)结构
|
||||
顺序存储
|
||||
链式存储
|
||||
索引存储
|
||||
散列(Hash)存储
|
||||
数据的运算
|
||||
```
|
||||
|
||||
|
||||
### 定义
|
||||
|
||||
线性表是具有相同数据类型的n(n≥0)个数据元素的有限序列。一般表示:
|
||||
|
||||
@@ -1,7 +1,17 @@
|
||||
|
||||
# 线性表的顺序表示
|
||||
|
||||
|
||||
```mindmap
|
||||
root(数据结构三要素)
|
||||
逻辑结构
|
||||
存储(物理)结构
|
||||
顺序存储
|
||||
链式存储
|
||||
索引存储
|
||||
散列(Hash)存储
|
||||
数据的运算
|
||||
```
|
||||
|
||||
### 定义
|
||||
|
||||
`顺序表`:顺序存储的线性表,**是用一组地址连续的存储单元,依次存储线性表中的数据元素,使得在逻辑上相邻的两个元素在物理位置上也相邻。**
|
||||
@@ -94,7 +104,7 @@ L.data=new ElemType[InitSize];
|
||||
|
||||
注意:先判空和临界值,提高算法健壮性
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
/*
|
||||
* 顺序表的插入操作
|
||||
@@ -151,7 +161,7 @@ bool ListInsert(SqList &L, int i, ElemType e){
|
||||
- 成功,返回true,将被删除的元素用引用变量返回;
|
||||
- 失败,返回false
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
/*
|
||||
* 顺序表的删除操作
|
||||
@@ -211,7 +221,7 @@ bool ListDelete(SqList &L, int i, ElemType &e){
|
||||
在顺序表L中查找第一个元素值等于e的元素,并返回位序
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
/*
|
||||
* @Description: 顺序表的按值查找(顺序查找)
|
||||
* @Version: Beta1.0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# 线性表的链式表示
|
||||
# 链式表示-单链表
|
||||
|
||||
顺序表的插入、删除操作需要移动大量元素,影响了运行效率(虽然时间复杂度为O(1)的情况也存在)。
|
||||
|
||||
@@ -19,6 +19,20 @@
|
||||
|
||||
|
||||
|
||||
```mindmap
|
||||
root(单链表)
|
||||
单链表
|
||||
头结点
|
||||
头插法
|
||||
尾插法
|
||||
按序号查找
|
||||
按值查找
|
||||
结点插入
|
||||
删除结点
|
||||
计算表长
|
||||
```
|
||||
|
||||
|
||||
### 单链表
|
||||
|
||||
线性表的链式存储称作`单链表`,通过**一组任意的存储单元**来存储线性表中的数据元素。
|
||||
@@ -28,7 +42,7 @@
|
||||
|
||||
|
||||
单链表中结点类型的描述:
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
// 单链表结点类型定义
|
||||
typeof struct LNode{
|
||||
@@ -79,7 +93,7 @@ typeof struct LNode{
|
||||
> 从空表开始,生成新的结点,将读取的数据存放在新结点的数据域中,将新结点插入到当前链表的表头【头结点之后】
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
/*
|
||||
* @Description: 单链表头插法创建
|
||||
* @Version: Beta1.0
|
||||
@@ -130,7 +144,7 @@ LinkList CreateListWithStartNode(LinkList &L){
|
||||
>新结点插入到当前链表的表尾上,必须增加一个尾指针r,始终指向当前链表的尾结点;
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
/*
|
||||
* @Description: 单链表尾插法创建
|
||||
@@ -189,7 +203,7 @@ LinkList CreateListWithEndNode(LinkList &L){
|
||||
> 在单链表中从第一个结点出发,顺指针next域逐个往下搜索、遍历,直到找出第i个结点为止,否则返回最后一个结点指针域NULL
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
/*
|
||||
* @Description: 单链表按序号查找
|
||||
@@ -243,7 +257,7 @@ LNode *GetElem(LinkList L,int i){
|
||||
> 从单链表的第一个结点开始,从前往后依次比较表中个结点数据域的值,等于给定值e,则返回该结点的指针;若整个单链表【遍历完】中没有数据域值为e的结点,则返回NULL;
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
/*
|
||||
* @Description: 单链表按值查找
|
||||
@@ -284,7 +298,7 @@ LNode *LocateElem(LinkList L,ElemType e){
|
||||
- 第二步: 找到待插入位置的前驱结点,即第(i-1)个结点;
|
||||
- 第三部: 在前驱结点后插入新结点;
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
// 循环遍历,时间复杂度O(n)
|
||||
p=GetElem(L,i-1);
|
||||
|
||||
@@ -322,7 +336,7 @@ LNode *LocateElem(LinkList L,ElemType e){
|
||||
> 在某结点的后面插入一个新的结点,单链表插入算法中,通常采用后插操作的
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
// 结点s插入到结点p的前面,修改指针域,顺序不能改变
|
||||
s->next=p->next;
|
||||
@@ -349,7 +363,7 @@ s->data=temp;
|
||||
- 第三步: 移动指针,删除结点元素;
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
// 获取删除位置结点元素的前驱结点
|
||||
p=GetElem(L,i-1);
|
||||
@@ -380,7 +394,7 @@ free(q)
|
||||
- 第四步:释放q的内存空间
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
// 存放p的后继结点指针
|
||||
q=p->next;
|
||||
|
||||
@@ -411,7 +425,7 @@ free(q)
|
||||
|
||||
不带头结点的单链表,当表为空时候,需要单独处理;
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
// 不带头结点的单链表L为空,判定条件是L=NULL。
|
||||
if(L===NULL){
|
||||
// 链表为空,表长为0
|
||||
@@ -425,4 +439,7 @@ if(L->next===NULL){
|
||||
return 0;
|
||||
}
|
||||
|
||||
```
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# # 链式表示-双链表
|
||||
|
||||
# 双链表
|
||||
|
||||
```mindmap
|
||||
root(双链表)
|
||||
基本特点
|
||||
插入结点
|
||||
删除结点
|
||||
```
|
||||
|
||||
|
||||
从单链表的结构上来看
|
||||
@@ -17,8 +24,7 @@
|
||||
- `next指针域` 指向结点的后继结点
|
||||
|
||||
|
||||
```C++
|
||||
|
||||
```cpp
|
||||
// 双链表结点类型
|
||||
typedef struct DNode{
|
||||
ElemType data; // 结点的数据域
|
||||
@@ -43,7 +49,7 @@ typedef struct DNode{
|
||||
> 在双链表中p所指的结点之后插入结点s
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
// 第一步
|
||||
s->next=p->next;
|
||||
@@ -65,7 +71,7 @@ p->next=s
|
||||
|
||||
> 删除双链表中结点p的后继结点q
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
// 第一步
|
||||
p->next=q->next;
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
# 链式表示-循环链表
|
||||
|
||||
# 循环链表
|
||||
|
||||
- 循环单链表
|
||||
- 循环双链表
|
||||
```mindmap
|
||||
循环链表
|
||||
循环单链表
|
||||
判空条件
|
||||
基本特点
|
||||
循环双链表
|
||||
判空条件
|
||||
基本特点
|
||||
|
||||
```
|
||||
|
||||
|
||||
### 循环单链表
|
||||
@@ -10,7 +18,7 @@
|
||||
`循环单链表`是在单链表的基础上,将最后一个结点(尾结点)的指针由`NULL`改为指向`头结点`,形成`环`。【单链表----->循环单链表】
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
// 双链表结点类型
|
||||
typedef struct DNode{
|
||||
ElemType data; // 结点的数据域
|
||||
@@ -42,7 +50,7 @@ typedef struct DNode{
|
||||
`循环双链表`是在双链表的基础上,将`尾结点`的`next`指针指向`头结点`,将`头结点`的`prior`指针指向`尾结点`。【双链表----->循环双链表】
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
// 双链表结点类型
|
||||
typedef struct DNode{
|
||||
ElemType data; // 结点的数据域
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# 链式表示-静态链表
|
||||
|
||||
# 静态链表
|
||||
```mindmap
|
||||
root(链式表示)
|
||||
单链表
|
||||
头结点
|
||||
头插法
|
||||
尾插法
|
||||
按序号查找
|
||||
按值查找
|
||||
结点插入
|
||||
删除结点
|
||||
计算表长
|
||||
```
|
||||
|
||||
> 借助数组来描述线性表的链式存储结构,结点元素同样存在数据域`data`和指针域`next`
|
||||
|
||||
@@ -13,7 +25,7 @@
|
||||
静态链表结构类型:
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
// 定义静态链表的最大长度
|
||||
# define MaxSize 50
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
# 顺序表和链表的比较
|
||||
|
||||
|
||||
### 存取方式
|
||||
|
||||
- 顺序表支持顺序存取和随机存取;
|
||||
- 链表只能从表头顺序存取元素,支持顺序存取;
|
||||
|
||||
|
||||
### 逻辑结构与物理结构
|
||||
|
||||
|
||||
- 顺序存储时,逻辑上相邻的元素,对应的物理存储位置也相邻【一定性】。
|
||||
- 链式存储时,逻辑上相邻的元素,对应的物理存储位置不一定相邻【可以相邻,也可以不相邻】。
|
||||
- 链式存储的逻辑关系通过指针链接表示;
|
||||
|
||||
|
||||
|
||||
### 时间复杂度
|
||||
|
||||
#### 按值查找
|
||||
|
||||
- 顺序表无序的情况下,顺序表和链表的时间复杂度均为O(n)
|
||||
- 顺序表有序的情况下,顺序表的时间复杂度为O(log<sub>2</sub>n),链表的时间复杂度为O(n);
|
||||
|
||||
|
||||
**注意:O(log<sub>2</sub>n) < O(n)**
|
||||
|
||||
#### 按序号查找
|
||||
|
||||
- 顺序表支持随机访问,时间复杂度为O(1);
|
||||
- 顺序表不支持随机访问,时间复杂度为O(n);
|
||||
|
||||
#### 插入、删除
|
||||
|
||||
- 顺序表平均需要移动半个表长的元素;
|
||||
- 链表只需要修改相应结点的指针域,不需要移动元素;
|
||||
- 链表结点除了数据域,还有指针域,在存储空间上比顺序存储需要更大的存储空间,付出更大的存储代价,存储密度不够大
|
||||
|
||||
### 空间分配
|
||||
|
||||
#### 顺序存储
|
||||
|
||||
|
||||
##### 静态分配
|
||||
|
||||
- 需要预先分配足够大的存储空间;
|
||||
- 空间装满后不能扩充,存储新元素将出现`内存溢出`;
|
||||
- 存储空间过大,顺序表后部闲置空间过多,造成`内部碎片`
|
||||
- 存储空间过小,会造成`内存溢出`
|
||||
|
||||
##### 动态分配
|
||||
|
||||
- 动态分配能够扩充存储空间,但需要移动大量元素,操作效率降低
|
||||
- 内存中没有更大块的连续存储空间,将会导致空间分配失败;
|
||||
|
||||
|
||||
#### 链式存储
|
||||
|
||||
- 链式存储的结点空间只在需要的时候申请分配
|
||||
- 只要内存由空间就可以分配,操作灵活、高效
|
||||
133
docs/manuscripts/ds/linear-table/7.总结.md
Normal file
133
docs/manuscripts/ds/linear-table/7.总结.md
Normal file
@@ -0,0 +1,133 @@
|
||||
# 线性表总结
|
||||
|
||||
```mindmap
|
||||
root(链式表示)
|
||||
顺序表和链表的比较
|
||||
存取方式
|
||||
逻辑结构与物理结构
|
||||
时间复杂度
|
||||
空间分配
|
||||
存储结构的选取
|
||||
基于存储的考虑
|
||||
基于运算的考虑
|
||||
基于环境的考虑
|
||||
知识补充
|
||||
单链表设置头结点
|
||||
```
|
||||
|
||||
## 顺序表和链表的比较
|
||||
|
||||
|
||||
### 存取方式
|
||||
|
||||
- 顺序表支持顺序存取和随机存取;
|
||||
- 链表只能从表头顺序存取元素,支持顺序存取;
|
||||
|
||||
|
||||
### 逻辑结构与物理结构
|
||||
|
||||
|
||||
- 顺序存储时,逻辑上相邻的元素,对应的物理存储位置也相邻【一定性】。
|
||||
- 链式存储时,逻辑上相邻的元素,对应的物理存储位置不一定相邻【可以相邻,也可以不相邻】。
|
||||
- 链式存储的逻辑关系通过指针链接表示;
|
||||
|
||||
|
||||
|
||||
### 时间复杂度
|
||||
|
||||
#### 按值查找
|
||||
|
||||
- 顺序表无序的情况下,顺序表和链表的时间复杂度均为O(n)
|
||||
- 顺序表有序的情况下,顺序表的时间复杂度为O(log<sub>2</sub>n),链表的时间复杂度为O(n);
|
||||
|
||||
|
||||
**注意:O(log<sub>2</sub>n) < O(n)**
|
||||
|
||||
#### 按序号查找
|
||||
|
||||
- 顺序表支持随机访问,时间复杂度为O(1);
|
||||
- 顺序表不支持随机访问,时间复杂度为O(n);
|
||||
|
||||
#### 插入、删除
|
||||
|
||||
- 顺序表平均需要移动半个表长的元素;
|
||||
- 链表只需要修改相应结点的指针域,不需要移动元素;
|
||||
- 链表结点除了数据域,还有指针域,在存储空间上比顺序存储需要更大的存储空间,付出更大的存储代价,存储密度不够大
|
||||
|
||||
### 空间分配
|
||||
|
||||
#### 顺序存储
|
||||
|
||||
|
||||
##### 静态分配
|
||||
|
||||
- 需要预先分配足够大的存储空间;
|
||||
- 空间装满后不能扩充,存储新元素将出现`内存溢出`;
|
||||
- 存储空间过大,顺序表后部闲置空间过多,造成`内部碎片`
|
||||
- 存储空间过小,会造成`内存溢出`
|
||||
|
||||
##### 动态分配
|
||||
|
||||
- 动态分配能够扩充存储空间,但需要移动大量元素,操作效率降低
|
||||
- 内存中没有更大块的连续存储空间,将会导致空间分配失败;
|
||||
|
||||
|
||||
#### 链式存储
|
||||
|
||||
- 链式存储的结点空间只在需要的时候申请分配
|
||||
- 只要内存由空间就可以分配,操作灵活、高效
|
||||
|
||||
## 存储结构的选取
|
||||
|
||||
### 基于存储的考虑
|
||||
|
||||
- 对线性表的长度和存储规模难以估计时,不宜采用顺序表存储
|
||||
- 链表不用事先估计存储规模,但存储密度较低
|
||||
- 链式存储结构的存储密度小于1,不要求连续的存储空间
|
||||
|
||||
|
||||
|
||||
### 基于运算的考虑
|
||||
|
||||
- 顺序表支持随机存取,按序号查找顺序表的时间复杂度为O(1);
|
||||
- 链表不支持随机存取,按序号查找链表的时间复杂度为O(n);
|
||||
- 顺序表的插入、删除操作,平均需要移动表中一半的元素,当表的数据量较大时,这种情况需要重点考虑的。
|
||||
- 链表的插入、删除操作,也是需要找插入位置(前驱结点、后继结点),主要的操作还是比较操作,相对较好;
|
||||
|
||||
|
||||
|
||||
|
||||
### 基于环境的考虑
|
||||
|
||||
- 顺序表容易实现,任何高级语言中都有数组类型;
|
||||
- 链表操作是基于指针的,指针移动,相对复杂;
|
||||
|
||||
|
||||
|
||||
综上比较
|
||||
|
||||
- 通常比较稳定的线性表选择顺序存储;
|
||||
- 频繁进行插入、删除操作的线性表,应该选择链式存储,动态性较强
|
||||
|
||||
## 知识补充
|
||||
|
||||
- 无论是链表的插入还是删除操作,必须保证不断链【重要】
|
||||
- 顺序存储结构可以随机存取也能顺序存取,链式结构只能进行顺序存取
|
||||
- 顺序存储方式同样适合图和树的存储,例如:满二叉树的顺序存储
|
||||
- 队列需要在表头删除元素,在表尾插入元素【先进先出】,采用带尾指针的循环单链表比较方便
|
||||
- 数组排序最少时间复杂度为O(nlog<sub>2</sub>n)【重要】
|
||||
- 静态链表中的指针称为`游标`,指示下一个元素在数组中的`下标`
|
||||
- 静态链表用数组表示,需要预先分配较大的连续空间,同时具有一般链表的特点(插入、删除元素不需要移动元素)
|
||||
|
||||
|
||||
### 单链表设置头结点
|
||||
|
||||
|
||||
目的
|
||||
|
||||
> 主要是方便运算。
|
||||
|
||||
好处
|
||||
|
||||
- 有头结点后,插入、删除数据元素的算法统一起来了,不需要判断是否在第一个元素之前插入或者删除第一个元素了。
|
||||
- 不论链表是否为空,头指针是指向头结点的`非空指针`,链表的头指针不变,因此空链表和非空链表的处理也就统一起来了。
|
||||
@@ -1,31 +0,0 @@
|
||||
# 存储结构的选取
|
||||
|
||||
### 基于存储的考虑
|
||||
|
||||
- 对线性表的长度和存储规模难以估计时,不宜采用顺序表存储
|
||||
- 链表不用事先估计存储规模,但存储密度较低
|
||||
- 链式存储结构的存储密度小于1,不要求连续的存储空间
|
||||
|
||||
|
||||
|
||||
### 基于运算的考虑
|
||||
|
||||
- 顺序表支持随机存取,按序号查找顺序表的时间复杂度为O(1);
|
||||
- 链表不支持随机存取,按序号查找链表的时间复杂度为O(n);
|
||||
- 顺序表的插入、删除操作,平均需要移动表中一半的元素,当表的数据量较大时,这种情况需要重点考虑的。
|
||||
- 链表的插入、删除操作,也是需要找插入位置(前驱结点、后继结点),主要的操作还是比较操作,相对较好;
|
||||
|
||||
|
||||
|
||||
|
||||
### 基于环境的考虑
|
||||
|
||||
- 顺序表容易实现,任何高级语言中都有数组类型;
|
||||
- 链表操作是基于指针的,指针移动,相对复杂;
|
||||
|
||||
|
||||
|
||||
综上比较
|
||||
|
||||
- 通常比较稳定的线性表选择顺序存储;
|
||||
- 频繁进行插入、删除操作的线性表,应该选择链式存储,动态性较强
|
||||
@@ -1,22 +0,0 @@
|
||||
# 零碎知识补充
|
||||
|
||||
- 无论是链表的插入还是删除操作,必须保证不断链【重要】
|
||||
- 顺序存储结构可以随机存取也能顺序存取,链式结构只能进行顺序存取
|
||||
- 顺序存储方式同样适合图和树的存储,例如:满二叉树的顺序存储
|
||||
- 队列需要在表头删除元素,在表尾插入元素【先进先出】,采用带尾指针的循环单链表比较方便
|
||||
- 数组排序最少时间复杂度为O(nlog<sub>2</sub>n)【重要】
|
||||
- 静态链表中的指针称为`游标`,指示下一个元素在数组中的`下标`
|
||||
- 静态链表用数组表示,需要预先分配较大的连续空间,同时具有一般链表的特点(插入、删除元素不需要移动元素)
|
||||
|
||||
|
||||
### 单链表设置头结点
|
||||
|
||||
|
||||
目的
|
||||
|
||||
> 主要是方便运算。
|
||||
|
||||
好处
|
||||
|
||||
- 有头结点后,插入、删除数据元素的算法统一起来了,不需要判断是否在第一个元素之前插入或者删除第一个元素了。
|
||||
- 不论链表是否为空,头指针是指向头结点的`非空指针`,链表的头指针不变,因此空链表和非空链表的处理也就统一起来了。
|
||||
@@ -1,20 +1,5 @@
|
||||
---
|
||||
title: 线性表
|
||||
---
|
||||
|
||||
|
||||
<!--
|
||||
* @Description:
|
||||
* @Version: Beta1.0
|
||||
* @Author: 【B站&公众号】Rong姐姐好可爱
|
||||
* @Date: 2021-03-07 21:57:27
|
||||
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
|
||||
* @LastEditTime: 2022-04-23 18:22:12
|
||||
-->
|
||||
|
||||
|
||||
|
||||
<!-- ## 线性表的基础概念和基本操作 -->
|
||||
# 线性表
|
||||
|
||||
## 基础概念和基本操作
|
||||
|
||||
@@ -222,7 +207,7 @@ bool ListInsert(SqList &L, int i, ElemType e){
|
||||
- 成功,返回true,将被删除的元素用引用变量返回;
|
||||
- 失败,返回false
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
/*
|
||||
* @Description: 顺序表的删除操作
|
||||
|
||||
@@ -1,332 +1,76 @@
|
||||
---
|
||||
title: 线性表
|
||||
---
|
||||
# 数据结构
|
||||
|
||||
```mindmap
|
||||
root(数据结构)
|
||||
|
||||
<!--
|
||||
* @Description:
|
||||
* @Version: Beta1.0
|
||||
* @Author: 【B站&公众号】Rong姐姐好可爱
|
||||
* @Date: 2021-03-07 21:57:27
|
||||
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
|
||||
* @LastEditTime: 2022-04-23 18:22:12
|
||||
-->
|
||||
|
||||
|
||||
|
||||
<!-- ## 线性表的基础概念和基本操作 -->
|
||||
|
||||
## 基础概念和基本操作
|
||||
|
||||
> 强调线性表是一种逻辑结构,不是存储结构
|
||||
|
||||
|
||||
### 定义
|
||||
|
||||
线性表是具有相同数据类型的n(n≥0)个数据元素的有限序列。一般表示:
|
||||
|
||||
L=(a<sub>1</sub>,a<sub>2</sub>,a<sub>3</sub>......a<sub>n</sub>) 其中n可以理解为表长(线性表的长度),n=0时候,即表空
|
||||
|
||||
|
||||
- `表头元素`:线性表中唯一的“第一个”数据元素,例如a<sub>1</sub>
|
||||
- `表尾元素`:线性表中唯一的“最后一个”数据元素,例如a<sub>n</sub>
|
||||
|
||||
|
||||
重要逻辑特性:
|
||||
|
||||
- 除表头元素外,线性表中每个元素有且仅有一个`直接前驱`
|
||||
- 除表尾元素外,线性表中每个元素有且仅有一个`直接后继`
|
||||
|
||||
基于此,这种**线性有序的逻辑结构**,使得线性表的特点如下:
|
||||
|
||||
- 元素的**个数有限**(强调有限序列)
|
||||
- 元素在逻辑上具有**顺序性**,在序列中每个元素都是都有先后次序的
|
||||
- 元素都数据元素,**每个元素都是单个元素**
|
||||
- 元素的**数据类型都相同**(强调相同数据类型),每个数据元素占用相同大小的存储空间
|
||||
- 元素具有**抽象性**,仅仅讨论元素之间的逻辑关系,不需要去考虑元素究竟表示的什么内容
|
||||
|
||||
|
||||
> 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
|
||||
|
||||
```C++
|
||||
|
||||
/*
|
||||
* @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<L.length;i++){
|
||||
if(L.data[i]===e){
|
||||
// i是元素的角标,i+1是具体元素的位序号
|
||||
return i+1;
|
||||
}
|
||||
}
|
||||
基础入门
|
||||
基本概念
|
||||
数据结构三要素
|
||||
算法和算法评价
|
||||
|
||||
// 未命中,返回0,即:没有
|
||||
return 0;
|
||||
线性表
|
||||
基础概念和操作
|
||||
线性表的顺序表示
|
||||
基础概念和操作
|
||||
基础概念和操作
|
||||
基础概念和操作
|
||||
基础概念和操作
|
||||
基础概念和操作
|
||||
存储结构的选取
|
||||
零碎知识补充
|
||||
|
||||
}
|
||||
|
||||
栈和队列
|
||||
栈的基本概念和基本操作
|
||||
栈的基本概念和基本操作
|
||||
栈的顺序存储结构
|
||||
栈的链式存储结构
|
||||
队列的基本概念和操作
|
||||
队列的顺序存储结构
|
||||
队列的链式存储结构
|
||||
栈和队列的应用
|
||||
特殊矩阵的压缩存储
|
||||
```
|
||||
|
||||
注意理解`位序`的含义,即元素在线性表中的位置序号,角标为`i`(角标从0开始),对应的位序为`i+1`(位序从1开始)。当返回为0时,则直接代表没有`命中`;
|
||||
|
||||
### 基础入门
|
||||
|
||||
- [ ] 基本概念
|
||||
- [ ] 数据结构三要素
|
||||
- [ ] 算法和算法评价
|
||||
|
||||
|
||||
**时间复杂度:**
|
||||
### 线性表
|
||||
|
||||
- 最好情况:查找的元素在表头,只需要比较一次,循环成本最小,时间复杂度为O(1);
|
||||
- 最坏情况:查找的元素在表尾或者不存在,需要完整遍历,比较n次,时间复杂度为O(n);
|
||||
- 平均情况:随机查找表上的第i个(1≤i≤L.length)元素,平均次数为(n+1)/2,对应时间复杂度为O(n)
|
||||
- [ ] 基础概念和操作
|
||||
- [ ] 线性表的顺序表示
|
||||
- [ ] 基础概念和操作
|
||||
- [ ] 基础概念和操作
|
||||
- [ ] 基础概念和操作
|
||||
- [ ] 基础概念和操作
|
||||
- [ ] 基础概念和操作
|
||||
- [ ] 存储结构的选取
|
||||
- [ ] 零碎知识补充
|
||||
|
||||
### 栈和队列
|
||||
|
||||
|
||||
#### 栈
|
||||
|
||||
- [ ] 栈的基本概念和基本操作
|
||||
- [ ] 栈的基本概念和基本操作
|
||||
- [ ] 栈的顺序存储结构
|
||||
- [ ] 栈的链式存储结构
|
||||
|
||||
#### 队列
|
||||
|
||||
- [ ] 队列的基本概念和操作
|
||||
- [ ] 队列的顺序存储结构
|
||||
- [ ] 队列的链式存储结构
|
||||
- [ ] 栈和队列的应用
|
||||
- [ ] 特殊矩阵的压缩存储
|
||||
|
||||
**线性表按值查找(顺序查找)的平均时间复杂度为O(n);**
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
**顺序存取是读写方式,不是存储结构;顺序存储是存储结构,包括有:顺序存储、链式存储、索引存储、散列存储**
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
### 顺序栈的存储类型
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
// 定义栈中元素的最大个数
|
||||
# define MaxSize 50
|
||||
|
||||
@@ -58,7 +58,7 @@ typedef struct{
|
||||
|
||||
`InitStack(&S)`: 初始化一个空栈`S`,栈顶指针初始化为-1
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
void InitStack(&S){
|
||||
// 栈顶指针-1
|
||||
@@ -72,7 +72,7 @@ void InitStack(&S){
|
||||
|
||||
`StackEmpty(S)`: 判断一个栈是否为空,即:栈顶指针是否为-1,如果栈空则返回`true`,否则返回`false`
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
bool StackEmpty(S){
|
||||
if(S.top==-1){
|
||||
@@ -92,7 +92,7 @@ bool StackEmpty(S){
|
||||
|
||||
`Push(&S,x)`: 进栈,若栈未满,`x`进栈操作,插入到栈内成为`新的栈顶元素`。
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
bool Push(SqStack &S,ElemType x){
|
||||
if(S.top==MaxSize-1){
|
||||
@@ -117,7 +117,7 @@ bool Push(SqStack &S,ElemType x){
|
||||
|
||||
`Pop(&S,&x)`: 出栈,若栈非空,出栈操作,**弹出栈顶元素**,用指针`x`进行返回。
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
bool Pop(SqStack &S,ElemType &x){
|
||||
if(S.top==-1){
|
||||
@@ -145,7 +145,7 @@ bool Pop(SqStack &S,ElemType &x){
|
||||
|
||||
`GetTop(S,&x)`: 读栈顶元素,若栈`S`非空,用x返回栈顶元素。
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
bool GetTop(SqStack S,ElemType &x){
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
栈的链式存储类型:
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
// 链栈类型定义【基础】
|
||||
typedef struct LinkNode{
|
||||
@@ -56,7 +56,7 @@ typedef struct LinkStack
|
||||
- 如果链栈存在,进行单链表的结点插入操作,移动指针,结点元素赋值,再将结点压入链栈中,移动链栈栈顶指针,最后链栈元素总数+1,返回true
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
/*
|
||||
* @Description: 基于单链表链栈的进栈操作
|
||||
@@ -94,7 +94,7 @@ bool linkStackPushNode(LinkStack* linkStack,int e){
|
||||
- 如果链栈满足出栈条件,则通过栈顶指针获取到链栈栈底结点,将其数据域赋值给变量e,移动栈顶指针指向待出栈元素的后继结点,同时释放待出栈元素的内存空间,链栈元素总数-1 ,出栈成功,返回true.
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
/*
|
||||
* @Description: 基于单链表链栈的出栈操作
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
队列顺序存储类型:
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
// 队列最大存储元素个数
|
||||
#define MaxSize 50
|
||||
|
||||
@@ -163,7 +163,7 @@ typedef struct {
|
||||
|
||||
|
||||
对应的算法实现:
|
||||
```C++
|
||||
```cpp
|
||||
// 入队算法
|
||||
// 尾插法:Q.data[Q.rear]=x;Q.rear=(Q.rear+1)%Maxsize;Q.tag=1
|
||||
// 队空条件:Q.front== Q.rear且Q.tag==0
|
||||
@@ -203,7 +203,7 @@ int DeLoopQueue(SqQueue &Q, ElemType &x){
|
||||
#### 初始化空队列
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
/*
|
||||
* @Description: 循环队列初始化,队列为空
|
||||
* @Version: Beta1.0
|
||||
@@ -219,7 +219,7 @@ void InitLoopQueque(&Q){
|
||||
|
||||
#### 队列是否为空
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
/*
|
||||
* @Description: 判断循环队列是否为空
|
||||
* @Version: Beta1.0
|
||||
@@ -244,7 +244,7 @@ bool isEmpatyLoopQueue(Q){
|
||||
|
||||
#### 入队操作
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
/*
|
||||
* @Description: 循环队列元素入队
|
||||
* @Version: Beta1.0
|
||||
@@ -276,7 +276,7 @@ bool EnLoopQueue(SqQueue &Q, ElemType x){
|
||||
#### 出队操作
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
/*
|
||||
* @Description: 循环队列元素出队
|
||||
@@ -311,7 +311,7 @@ bool DeLoopQueue(SqQueue &Q, ElemType &x){
|
||||
|
||||
#### 获取队头元素
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
/*
|
||||
* @Description: 获取循环队列队头元素
|
||||
* @Version: Beta1.0
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
队列的链式存储结构:
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
// 链式队列节点定义
|
||||
typedef struct{
|
||||
// 结果点数据域
|
||||
@@ -65,7 +65,7 @@ typedef struct{
|
||||
|
||||
#### 队列初始化
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
/*
|
||||
* @Description: 链式队列初始化
|
||||
@@ -91,7 +91,7 @@ voide InitLinkQueue(LinkQueue &Q){
|
||||
|
||||
#### 判断队空
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
/*
|
||||
* @Description: 判断链式队列是否为空
|
||||
@@ -115,7 +115,7 @@ bool IsEmptyLinkQueue(LinkQueue Q){
|
||||
#### 入队
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
/*
|
||||
* @Description: 链式队列入队操作
|
||||
* @Version: Beta1.0
|
||||
@@ -145,7 +145,7 @@ void EnLinkQueue(LinkQueue &Q, ElemType x){
|
||||
|
||||
#### 出队
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
/*
|
||||
* @Description: 链式队列出队操作
|
||||
|
||||
@@ -1,7 +1,19 @@
|
||||
# 栈和队列的应用
|
||||
|
||||
# 栈和队列总结
|
||||
|
||||
> Tips: 这里不会做过多文字介绍相关应用,具体需要自己看书、查资料揣摩
|
||||
|
||||
|
||||
```mindmap
|
||||
root(栈和队列总结)
|
||||
栈的应用
|
||||
队列的应用
|
||||
数组的定义
|
||||
矩阵的压缩存储
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 栈的应用
|
||||
|
||||
- 括号匹配
|
||||
@@ -23,7 +35,7 @@
|
||||
经典的斐波拉切数列,可以用递归来实现:
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
// 定义递归函数,实现斐波拉切数列
|
||||
|
||||
@@ -44,7 +56,7 @@ int Fibonacci(n){
|
||||
上面很基础的代码,是分`n=0`和`n=1`的情况,先进行过滤,其他情况下则进行递归,其实在日常开发中,经常会有简化的函数封装
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
// 定义递归函数,实现斐波拉切数列
|
||||
|
||||
@@ -65,7 +77,7 @@ int Fibonacci(n){
|
||||
|
||||
可以采用空间换时间的思路,来降低算法的时间复杂度
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
// 使用非递归实现
|
||||
int Fibonacci(int n) {
|
||||
if(n < 2)
|
||||
@@ -123,4 +135,63 @@ int Fibonacci(n){
|
||||
- 解决由多用户引起的资源竞争问题(例如:操作系统中的进程调度...)
|
||||
|
||||
|
||||
其实,队列在计算机系统的中应用, 在看完操作系统那本书后,就会很好理解,建议学到这里,也去翻翻操作系统,汤晓丹那本很经典哟...
|
||||
其实,队列在计算机系统的中应用, 在看完操作系统那本书后,就会很好理解,建议学到这里,也去翻翻操作系统,汤晓丹那本很经典哟...
|
||||
|
||||
|
||||
# 特殊矩阵的压缩存储
|
||||
|
||||
> 这部分知识我个人觉得以了解为主,复习、学习的时候还是要以前面的部分为主!
|
||||
|
||||
矩阵在`计算机图形学`、`工程计算`中占有举足轻重的地位。
|
||||
|
||||
|
||||
### 数组的定义
|
||||
|
||||
`数组`: 由n(n≥1)个相同类型的数据元素构成的有限序列。
|
||||
|
||||
每个数据元素称为一个数组元素,同时每个元素受n个线性关系的约束,**每个元素在n个线性关系中的序号称为元素的`下标`**,称为该数组为n的数组。
|
||||
|
||||
数组和线性表的关系:
|
||||
- 数组是线性表的推广。
|
||||
- 数组一旦被定义,维数和维界就不再改变。
|
||||
- 除了结构的初始化和销毁外,数组只会有存取元素和修改元素的操作。
|
||||
|
||||
|
||||
一维数组可以看做是一个线性表
|
||||
|
||||
二维数组可以看做元素是线性表的线性表
|
||||
|
||||
....
|
||||
|
||||
|
||||
### 矩阵的压缩存储
|
||||
|
||||
|
||||
`压缩存储`:多个值相同的元素只分配一个存储空间,对零元素不分配存储空间---->节省存储空间。
|
||||
|
||||
`特殊矩阵`:具有很多相同矩阵元素或零元素,并且这些相同矩阵元素或零元素的分布有一定规律性的矩阵。
|
||||
|
||||
- 对称矩阵
|
||||
- 上、下三角矩阵
|
||||
- 对角矩阵(带状矩阵)
|
||||
- ....
|
||||
|
||||
这里如果学过线性代数这本书,其实也就很好理解(赶紧去把数学知识捡起来鸭,噗呲哈哈啊哈)
|
||||
|
||||
`稀疏矩阵`:矩阵元素个数s相对于矩阵中非零元素的个数t来说非常多、差距非常大,即`s>>t的矩阵`可以叫`稀疏矩阵`
|
||||
|
||||
|
||||
注意:
|
||||
|
||||
- 常规方法来存储稀疏矩阵,会想当浪费存储空间,所以稀疏矩阵只需要存储非零元素
|
||||
- 通常非零元素的分布是没有规律的,除了存储非零元素外,还需要存储元素所在位置的行和列
|
||||
- 寻相互存储三元组 `<行标,列表,值>`
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
三元组的结点存储了行标(row)、列表(col)、值(value)三种信息,是主要用来存储稀疏矩阵的一种数据结构。
|
||||
|
||||
|
||||
**注意:矩阵压缩存储的目的就是为了节省空间,已经存过的就不存或者少存(经典想法)**
|
||||
@@ -1,57 +0,0 @@
|
||||
# 特殊矩阵的压缩存储
|
||||
|
||||
> 这部分知识我个人觉得以了解为主,复习、学习的时候还是要以前面的部分为主!
|
||||
|
||||
矩阵在`计算机图形学`、`工程计算`中占有举足轻重的地位。
|
||||
|
||||
|
||||
### 数组的定义
|
||||
|
||||
`数组`: 由n(n≥1)个相同类型的数据元素构成的有限序列。
|
||||
|
||||
每个数据元素称为一个数组元素,同时每个元素受n个线性关系的约束,**每个元素在n个线性关系中的序号称为元素的`下标`**,称为该数组为n的数组。
|
||||
|
||||
数组和线性表的关系:
|
||||
- 数组是线性表的推广。
|
||||
- 数组一旦被定义,维数和维界就不再改变。
|
||||
- 除了结构的初始化和销毁外,数组只会有存取元素和修改元素的操作。
|
||||
|
||||
|
||||
一维数组可以看做是一个线性表
|
||||
|
||||
二维数组可以看做元素是线性表的线性表
|
||||
|
||||
....
|
||||
|
||||
|
||||
### 矩阵的压缩存储
|
||||
|
||||
|
||||
`压缩存储`:多个值相同的元素只分配一个存储空间,对零元素不分配存储空间---->节省存储空间。
|
||||
|
||||
`特殊矩阵`:具有很多相同矩阵元素或零元素,并且这些相同矩阵元素或零元素的分布有一定规律性的矩阵。
|
||||
|
||||
- 对称矩阵
|
||||
- 上、下三角矩阵
|
||||
- 对角矩阵(带状矩阵)
|
||||
- ....
|
||||
|
||||
这里如果学过线性代数这本书,其实也就很好理解(赶紧去把数学知识捡起来鸭,噗呲哈哈啊哈)
|
||||
|
||||
`稀疏矩阵`:矩阵元素个数s相对于矩阵中非零元素的个数t来说非常多、差距非常大,即`s>>t的矩阵`可以叫`稀疏矩阵`
|
||||
|
||||
|
||||
注意:
|
||||
|
||||
- 常规方法来存储稀疏矩阵,会想当浪费存储空间,所以稀疏矩阵只需要存储非零元素
|
||||
- 通常非零元素的分布是没有规律的,除了存储非零元素外,还需要存储元素所在位置的行和列
|
||||
- 寻相互存储三元组 `<行标,列表,值>`
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
三元组的结点存储了行标(row)、列表(col)、值(value)三种信息,是主要用来存储稀疏矩阵的一种数据结构。
|
||||
|
||||
|
||||
**注意:矩阵压缩存储的目的就是为了节省空间,已经存过的就不存或者少存(经典想法)**
|
||||
@@ -1,14 +1,3 @@
|
||||
# 栈和队列
|
||||
|
||||
### 主要内容
|
||||
|
||||
- [栈的基本概念和基本操作](1.栈的基本概念和基本操作.md)
|
||||
- [栈的顺序存储结构](2.栈的顺序存储结构.md)
|
||||
- [栈的链式存储结构](3.栈的链式存储结构.md)
|
||||
- [队列的基本概念和基础操作](4.队列的基本概念和操作.md)
|
||||
- [队列的顺序存储](5.队列的顺序存储结构.md)
|
||||
- [队列的链式存储](6.队列的链式存储结构.md)
|
||||
- [栈和队列的应用](7.栈和队列的应用.md)
|
||||
- [特殊矩阵的压缩存储](8.特殊矩阵的压缩存储.md)
|
||||
|
||||

|
||||
@@ -1,6 +1,52 @@
|
||||
export const osSidebar = [
|
||||
{
|
||||
text: '操作系统',
|
||||
children: []
|
||||
text: '系统概述',
|
||||
prefix: '系统概述',
|
||||
children: [
|
||||
{
|
||||
text: '引论',
|
||||
link: '1.操作系统引论.md'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
text: '进程管理',
|
||||
prefix: '进程管理',
|
||||
children: [
|
||||
{
|
||||
text: '基本概念',
|
||||
link: '1.basic_concepts.md'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
text: '内存管理',
|
||||
prefix: '内存管理',
|
||||
children: [
|
||||
{
|
||||
text: '基本概念',
|
||||
link: '1.basic_concepts.md'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
text: '文件管理',
|
||||
prefix: '文件管理',
|
||||
children: [
|
||||
{
|
||||
text: '基本概念',
|
||||
link: '1.basic_concepts.md'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
text: 'I/O管理',
|
||||
prefix: 'I/O管理',
|
||||
children: [
|
||||
{
|
||||
text: '基本概念',
|
||||
link: '1.basic_concepts.md'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
|
||||
# 操作系统
|
||||
doing
|
||||
|
||||
|
||||
```mindmap
|
||||
操作系统
|
||||
系统概述
|
||||
|
||||
进程管理
|
||||
|
||||
进程管理
|
||||
|
||||
文件管理
|
||||
|
||||
I/O管理
|
||||
```
|
||||
268
docs/manuscripts/os/系统概述/1.操作系统引论.md
Normal file
268
docs/manuscripts/os/系统概述/1.操作系统引论.md
Normal file
@@ -0,0 +1,268 @@
|
||||
# 操作系统引论
|
||||
|
||||
## 操作系统的目标和作用
|
||||
|
||||
> 操作系统(Operating System)是配置在计算机硬件上的第一层软件,是对硬件系统的首次扩充。主要作用是管理好硬件设备,提高硬件设备的利用率的系统吞吐量,并为用户和应用程序提供简单的接口,便于用户使用。**操作系统是现在计算机系统中最基本和最重要的系统软件**
|
||||
|
||||
### 操作系统的目标
|
||||
|
||||
> 方便性、有效性、可扩充性、开放性
|
||||
|
||||
#### 方便性
|
||||
|
||||
未配置操作系统(OS)的计算机系统必然是极难使用,不适合大多数人的,当用户需要在计算机硬件(裸机)上运行自己写的程序,就必须使用到机器语言。倘若计算机硬件配置了OS,系统便可以使用编译命令将用户采用高级语言(Java、C、C++等)编写的程序翻译成机器可执行代码,亦或通过OS所提供的各种命令来操作计算机系统,从而极大的方便用户,计算机也能更加易于使用和学习;
|
||||
|
||||
#### 有效性
|
||||
|
||||
操作系统的有效性包含的第一层含义是**提高系统资源的利用率**,同时,提高系统资源利用率也是推动OS发展最主要的动力;
|
||||
|
||||
另一层含义是**提高系统的吞吐量**,操作系统可以通过合理地组织计算机的工作流程, 加速程序的运行,速断程序的运行周期,以此来提高系统的吞吐量;
|
||||
|
||||
**方便性和有效性是设计OS时最重要的两个目标**
|
||||
|
||||
#### 可扩充性
|
||||
|
||||
为适应计算机硬件、体系结构以及计算机应用发展的要求,操作系统必须具有很好的可扩充性。然而操作系统的可扩充性好坏与OS的结构有着十分紧密的联系。正是因为如此,操作系统的发展也推动着操作系统接口的不断发展;
|
||||
|
||||
无结构系统------>模块化结构----->层次化结构----->微内核结构
|
||||
|
||||
#### 开放性
|
||||
|
||||
随着Internet的快速发展,计算机操作系统的应用环境也由单机环境转向了网络环境,应用环境需要更加的开放,因此对操作系统的开放性提出了更高的要求;
|
||||
|
||||
所谓开放性,即指系统能遵循世界标准规范,特别是遵循开放系统互连OSI国际标准;而事实上,方式遵循国际标准所开发的硬件和软件,都能够彼此兼容,方便实现互连。开放性是衡量一个新推出的系统或者软件能否被广泛应用的至关重要的因素
|
||||
|
||||
### 操作系统的作用
|
||||
|
||||
> 操作系统在计算机系统中所起的作用,可以从用户、资源管理、资源抽象等不通维度去分析和讨论
|
||||
>
|
||||
> - 作为用户与计算机硬件系统的接口
|
||||
> - 作为计算机系统资源的管理者
|
||||
> - 实现对计算机资源的抽象
|
||||
|
||||
#### 作为用户与计算机硬件系统的接口
|
||||
|
||||
操作系统作为用户与计算机硬件与系统之间接口的含义:**操作系统处于用户与计算机硬件系统之间,用户通过OS来使用计算机系统**,即:用户在操作系统的帮助下能够方便、快捷、可靠地操作计算机硬件和运行自己的程序。
|
||||
|
||||
从下图中可以看出,用户可以通过三种方式操作使用计算机
|
||||
|
||||
- 命令
|
||||
- 系统调用
|
||||
- 图标-窗口可视化
|
||||
|
||||

|
||||
|
||||
#### 作为计算机系统资源的管理者
|
||||
|
||||
操作系统的主要功能是管理多种硬件和软件资源,归纳起来分为:
|
||||
|
||||
- 处理机
|
||||
- 存储器
|
||||
- I/O设备
|
||||
- 文件(数据和程序)
|
||||
|
||||
四部分各司其职,同时操作系统必须为使用资源的请求进行**授权**,协调多用户对共享资源的使用,**避免发生冲突**;
|
||||
|
||||
- 处理机管理用于分配和控制处理机
|
||||
- 存储器管理主要负责内存的分配和回收
|
||||
- I/O设备管理是I/O设备的分配(回收)与操作
|
||||
- 文件管理是用于实现对文件的存取、共享和保护
|
||||
|
||||
#### 实现对计算机资源的抽象
|
||||
|
||||
从裸机(完全无软件的计算机系统)来看,仅仅向用户提供硬件接口(物理接口),用户必须对物理接口实现的细节有充分的了解,导致物理节气很难被广泛使用。**为了能够方便用户使用I/O设备,人们需要在裸机上覆盖一层I/O设备管理软件,借助I/O设备软件来实现对I/O设备操作的细节,并且需要向上将I/O设备抽象为一组数据结构以及一组操作命令(例:read命令、write命令)。使得用户通过利用这些数据结构及操作命令来进行数据输入和输出,不需要关系I/O设备在硬件上是如何实现的。**
|
||||
|
||||
> 在裸机上铺设的I/O软件隐藏了I/O设备的具体细节,向上提供了一组抽象的I/O设备
|
||||
|
||||

|
||||
|
||||
通常,将覆盖上述软件(I/O软件、物理接口、硬件)的机器成为`扩充机`或`虚拟机`,向用户提供一个对硬件操作的抽象模型。**I/O设备管理软件实现了对计算机硬件操作的第一层次的抽象;**
|
||||
|
||||
**操作系统是铺设在计算机硬件上的多层软件的集合,不仅增强了系统的功能,还隐藏了对硬件操作的具体细节,实现了对计算机硬件操作的多个层次的抽象模型。需要说明的是,不仅仅可以在底层对硬件资源加以抽象,还可以在高层对该资源底层已抽象的模型再次进行抽象,成为更加高层的抽象模型。**
|
||||
|
||||
> 随着抽象层次的提高,抽象接口所提供的功能也就越强,用户使用起来也越方便。
|
||||
|
||||
### 推动操作系统发展的主要动力
|
||||
|
||||
> 操作系统自20世纪50年代诞生后,经历了简单到复杂、低级到高级的发展,在60多年的时间里,操作系统在各方面都有长足的进步,能够很好的适应计算机硬件和体系结构的快速发展,以及应用需求下的不断变化。
|
||||
|
||||
主要推动力:
|
||||
|
||||
- 不断提高的计算机资源利用率
|
||||
- 方便用户
|
||||
- 器件的不断更新换代
|
||||
- 计算机体系结构的不断发展
|
||||
- 不断提出新的应用需求
|
||||
|
||||
### 操作系统的概念
|
||||
|
||||
在信息化是戴,软件被称作为计算机系统的灵魂,作为软件核心的操作系统,已经与现代计算机系统密不可分,融为一体。自下而上可错略的分为:硬件、操作系统、应用软件、用户(与组成原理中的分层不同)。**操作系统管理各种计算机硬件,为应用程序提供基础,并充当计算机硬件与用户之间的中介。控制和协调各用户的应用程序对硬件的分配和使用**
|
||||
|
||||
### 操作系统的定义
|
||||
|
||||
>操作系统(Operating System,OS)指控制和管理整个计算机系统的硬件和软件资源,并合理地组织调度计算机的工作和资源的分配,提供给用户和其他软件方便的接口和环境的程序集合。是最基础的系统软件
|
||||
|
||||
## 操作系统的发展过程
|
||||
|
||||
> 在20世纪50年代中期,出现了第一个简单的批处理操作系统;
|
||||
>
|
||||
> 60年代中期开发出多道程序批处理操作系统;
|
||||
>
|
||||
> 不久后,推出了分时操作系统,与此同时,用于工业 和武器控制的实时操作系统相继出现;
|
||||
>
|
||||
> 20世纪70到90年代,成为VLSI(超大规模集成电路)和计算机体系结构发展的年代,微型机、多处理机和计算机网络诞生并发展
|
||||
|
||||
### 操作系统发展历程
|
||||
|
||||

|
||||
|
||||
#### 手工操作阶段
|
||||
|
||||
首先明确,此阶段无操作系统;操作方式是由程序员将事先已经穿孔的纸带(卡片),装入纸带输入机(卡片输入机),在启动它们将纸带或者卡片上的程序和数据输入计算机。然后才能启动计算机运行。**仅当程序运行完毕并且取走结果后,才能允许下一个用户上机**,即存在着:程序装入、运行、结果的输出等操作。随着计算机硬件的快速发展,人机矛盾(处理速度和资源利用的矛盾)突显严重,从而促进操作系统的发展,寻求新的解决方法;
|
||||
|
||||
手工操作阶段有着明显的缺点:
|
||||
|
||||
- 用户独占主机(一台计算机的全部资源由上机用户所独占),不会出现因资源已经被其他用户占用而等待的现象,但是资源利用率低;
|
||||
- CPU等待手工操作,CPU及内存等资源是空闲的,资源利用也不充分
|
||||
|
||||
由于上面两个突显的缺点,**人工操作的方式严重降低了计算机资源的利用率,包括软件和硬件资源,即人机矛盾; ** 虽然CPU 的速度在迅速提高,但是I/O设备的比速度却提高 缓慢,使得CPU与I/O设备之间速度不匹配问题更加突出。**唯一的解决办法就是用高速的机器代替相对较慢的手工操作来对作业进行控制。** 先后出现过通道技术、缓冲技术、脱机输入/输出技术
|
||||
|
||||
#### 脱机处理阶段
|
||||
|
||||
为了能够充分地提高计算机的利用率,应该尽量的保持系统连续的运行,即在处理完一个作业任务后,紧接着处理下一个作业,减少机器空闲等待时间;
|
||||
|
||||

|
||||
|
||||
从上面的流程图中可以看出,单道批处理系统在解决人机矛盾和CPU与I/O设备速度不匹配矛盾的过程中形成的。**也可以理解为批处理系统的首要目的是在提高系统资源的利用率和系统吞吐量**。系统对作业的处理是成批进行的,内存中始终只能保存一道作业任务;
|
||||
|
||||
单道批处理系统的显著特征:
|
||||
|
||||
- **自动性** 在顺序的情况下,在磁带上的一批作业任务能够自动地逐个依次有序运行,不需要人工干预;
|
||||
- **顺序性** 各道作业都是顺序进入内存中,完成的顺序和进入内存顺序,在正常情况下是完全相同的。可以理解为类似队列的方式,先调入内存的作业先完成;
|
||||
- **单道性** 内存中仅仅只有一道程序运行,监督程序每次从磁带上只调用一道程序进入内存运行。只有当程序完成或者发生异常时,才会更换后续程序进入内存中;
|
||||
|
||||
单道批处理系统主要的缺点是:**系统中的资源得不到充分的利用**,程序在运行中发出I/O请求后,CPU便会处于等待状态,即:CPU空闲,同样也会造成内存的浪费;
|
||||
|
||||
#### 多道批处理系统
|
||||
|
||||
允许多个程序同时进入到内存并且运行。即同时会把多个程序放入内存,允许在CPU中交替运行。**共享系统中各种软件、硬件资源**。当一道程序因为I/O请求而暂停运行,CPU可以立即转去运行另外一道程序。最大程度的让系统的各个组成部分都“动起来”、“忙起来”,花费很少的时间去切换任务。实现系统各部件之间**并行工作,提高效率**;
|
||||
|
||||
多道程序设计的特点:**多道、宏观上并行、微观上串行**
|
||||
|
||||
>多道:计算机内存中同时存放多个相互独立的程序;
|
||||
>
|
||||
>宏观上并行:同时进入系统到的多道程序都处于运行过程中,先后开始了各自的运行,但是都处于运行中,没有运行完毕;
|
||||
>
|
||||
>微观上串行:内存中的多道程序轮流占有CPU资源,交替进行;
|
||||
|
||||
多道程序设计计算实现需要解决的问题:
|
||||
|
||||
- 如何分配处理器
|
||||
- 多道程序的内存分配问题
|
||||
- I/O设备如何分配
|
||||
- 如何组织和存放大量的程序和数据,便于用户使用和保证其安全性与一致性
|
||||
|
||||
**在批处理系统中采用多道程序设计技术,就形成了多道批处理操作系统**。由作业调度程序自动地选择作业运行;
|
||||
|
||||
多道批处理系统的优缺点:
|
||||
|
||||
- **资源利用率高**
|
||||
- **系统吞吐量大**
|
||||
- CPU和其他资源保持“忙碌”状态(主要原因);
|
||||
- 仅仅当作业完成时或者运行不下去时才进行切换,系统开销小(主要原因);
|
||||
|
||||
|
||||
- **平均周转时间长** 需要排队依次处理,响应时间长
|
||||
- **无交互能力** 修改和调试程序极其不方便,用户既不能了解程序的运行情况,也不能控制计算机;
|
||||
|
||||
#### 分时系统
|
||||
|
||||
> 推动多道批处理系统形成和发展的主要动力是**提高资源利用和系统吞吐量**;推动分时系统形成和发展的主要动力是**满足用户对人机交互的需求**,即满足:**人-机交互、共享主机**
|
||||
|
||||
分时系统定义:**在一台主机上连接了多个配有显示器和键盘的终端并由此组成的系统,允许多个用户同时通过自己的终端,以交互的方式使用计算机,共享主机中的资源**;
|
||||
|
||||
分时技术定义: 把处理器的运行时间分成很短的时间片,按照时间片轮转把处理器分配给各个联机作业使用。若某个作业在分配给它的时间片内不能完成计算,则该作业暂时停止运行,把处理器让给其他作业,等待下一轮再继续运行。**由于计算机速度很快,作业运行轮转也很快,给每个用户的实际的感觉就好像是独占一台计算机**
|
||||
|
||||
> 如何使用户能与自己的作业进行交互,是实现分时系统最关键的问题,需要处理好及时接收、及时处理的问题。
|
||||
|
||||
- 及时接收:可以通过在系统中配置多路卡,通过多路卡实现分时多路复用
|
||||
- 及时处理:关键是实现人-机交互,用户输入命令后 ,能够对自己的作业及其运行及时地**实施控制**或者**进行修改**。需要各用户的作业都必须驻留在内存中,并且可以频繁地获得处理机运行。推荐采用:**作业直接进入内存**或者**时间片轮转方式**
|
||||
|
||||
> 时间片简单解释:
|
||||
>
|
||||
> 一个时间片就是一段很短的时间,系统规定每个作业每次只能运行一个时间片,然后就暂停该作业的运行,通过调度算法立即调度到下一个作业运行。使得每个用户都能及时地与自己的作业进行交互,用户的请求得到及时的响应
|
||||
|
||||
**主要特征**
|
||||
|
||||
> 与多道批处理系统相比,分时系统具有非常明显的特性,包含:多路性、独立性、及时性、交互性
|
||||
|
||||
- **多路性** 也可以叫做同时性,允许多个终端同时使用一台计算机(即:一台计算机与若干的终端相连接,终端上的用户可以同时或者基本同时使用计算机)
|
||||
- **独立性** 系统中的多个用户可以彼此独立地进行操作,互不干扰,就像单独操作计算机一样,自我感觉为一人独用
|
||||
- **及时性** 用户的请求能够在很短时间(用户能够接收的时间间隔)内获得响应
|
||||
- **交互性** 用户能够方便地与系统进行人-机交互对话,用户通过终端采用人-机交互的方式控制程序运行调度,与程序进行交互
|
||||
|
||||
> 理解好交互对话中对话的含义,不是说像日常人们之间言语的对话,是通过人的操作来获取程序的响应,像领导发号命令会及时响应;
|
||||
>
|
||||
> **分时操作系统较好的解决人-机交互问题**
|
||||
|
||||
#### 实时系统
|
||||
|
||||
> 为了能够在某个时间限制内完成某些紧急任务而不需要进行时间片排队 ,诞生了**实时操作系统**,时间限制有两种情况:
|
||||
>
|
||||
> - 硬实时系统 规定某个动作必须绝对地在规定的时刻(或者规定的时间范围)发生
|
||||
> - 软实时系统 能够接受偶尔违反时间规定,并且不会引起任何永久性的损害
|
||||
>
|
||||
> 上面两种情况,可以类比公司的上班只读:硬实时----->打卡上班,讲究KPI ,软实时------>扁平管理,推崇OKR
|
||||
|
||||
在实时操作系统的控制下,计算机系统受到外部信号后及时进行处理,并且要在严格限制内完成接收的时间。**实时操作系统的主要特点是及时性和可靠性**
|
||||
|
||||
**实时系统的类型**
|
||||
|
||||
- 工业(武器)控制系统
|
||||
- 信号查询系统
|
||||
- 多媒体系统
|
||||
- 嵌入式系统
|
||||
|
||||
**实时任务的类型**
|
||||
|
||||
> 关于截止时间(DeadLine)也叫最后期限的理解:
|
||||
>
|
||||
> 1.开始截止时间,指某任务在某时间以前必须开始执行
|
||||
>
|
||||
> 2.完成截止时间,指某项任务在某时间以前必须完成
|
||||
|
||||
- 周期性实时任务和非周期性实时任务
|
||||
- 硬实时任务和软实时任务
|
||||
|
||||
|
||||
|
||||
## 操作系统的基本特性
|
||||
|
||||
> 例如批处理系统 有高资源利用率和系统吞吐量特点,分时系统能够获得及时响应,实时系统具有实时的特征。横向比较,不同操作系统之间功能具有**并发、共享、虚拟、异步**的基本特征
|
||||
|
||||
### 并发(Concurrence)
|
||||
|
||||
> 并发是指两个或者多个事件在**同一事件间隔**内发生。而并行是指两个或多个事件在**同一时刻**发生
|
||||
|
||||
在多道程序环境下,一段时间内宏观上有多道程序在同时执行,而在每一时刻,单处理机环境下实际仅仅能有一道程序在执行。因此,**微观上程序还是在分时地交替执行**,操作系统的并发性是通过分时去实现的。
|
||||
|
||||
在操作系统中,引入进程的目的就是为了让程序能够并发的执行,多个进程之间可以鬓发执行和交换信息
|
||||
|
||||
> 进程:在系统中能独立运行并作为**资源分配的基本单位**,由一组机器指令、数据和堆栈等组合而成,是一个能独立运行的活动实体
|
||||
|
||||
|
||||
|
||||
### 共享(Sharing)
|
||||
|
||||
### 虚拟(Virtual)
|
||||
|
||||
### 异步(Asynchronism)
|
||||
|
||||
## 操作系统的主要功能
|
||||
|
||||
> 为了能够给多道程序提供良好的运行环境,操作系统应该具有:处理机管理、存储器管理、设备管理、文件管理的功能,同时方便用户使用操作系统,还必须提供接口。
|
||||
>
|
||||
> 引入操作系统的主要目的:为多道程序的运行提供良好的运行环境,保证多道程序能够有条不紊地、高效地运行,最大程度的提高系统中各种资源的利用率
|
||||
|
||||
## 操作系统的结构设计
|
||||
|
||||
BIN
docs/manuscripts/os/系统概述/images/1593347649549.png
Normal file
BIN
docs/manuscripts/os/系统概述/images/1593347649549.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.0 KiB |
BIN
docs/manuscripts/os/系统概述/images/1593349066120.png
Normal file
BIN
docs/manuscripts/os/系统概述/images/1593349066120.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
BIN
docs/manuscripts/os/系统概述/images/20200701224119555.png
Normal file
BIN
docs/manuscripts/os/系统概述/images/20200701224119555.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 51 KiB |
BIN
docs/manuscripts/os/系统概述/images/os-time.png
Normal file
BIN
docs/manuscripts/os/系统概述/images/os-time.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 308 KiB |
@@ -117,7 +117,7 @@ bool Push(SqStack &S,ElemType x){
|
||||
|
||||
`Pop(&S,&x)`: 出栈,若栈非空,出栈操作,**弹出栈顶元素**,用指针`x`进行返回。
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
bool Pop(SqStack &S,ElemType &x){
|
||||
if(S.top==-1){
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
栈的链式存储类型:
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
// 链栈类型定义【基础】
|
||||
typedef struct LinkNode{
|
||||
@@ -56,7 +56,7 @@ typedef struct LinkStack
|
||||
- 如果链栈存在,进行单链表的结点插入操作,移动指针,结点元素赋值,再将结点压入链栈中,移动链栈栈顶指针,最后链栈元素总数+1,返回true
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
/*
|
||||
* @Description: 基于单链表链栈的进栈操作
|
||||
@@ -94,7 +94,7 @@ bool linkStackPushNode(LinkStack* linkStack,int e){
|
||||
- 如果链栈满足出栈条件,则通过栈顶指针获取到链栈栈底结点,将其数据域赋值给变量e,移动栈顶指针指向待出栈元素的后继结点,同时释放待出栈元素的内存空间,链栈元素总数-1 ,出栈成功,返回true.
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
/*
|
||||
* @Description: 基于单链表链栈的出栈操作
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
队列顺序存储类型:
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
// 队列最大存储元素个数
|
||||
#define MaxSize 50
|
||||
|
||||
@@ -163,7 +163,7 @@ typedef struct {
|
||||
|
||||
|
||||
对应的算法实现:
|
||||
```C++
|
||||
```cpp
|
||||
// 入队算法
|
||||
// 尾插法:Q.data[Q.rear]=x;Q.rear=(Q.rear+1)%Maxsize;Q.tag=1
|
||||
// 队空条件:Q.front== Q.rear且Q.tag==0
|
||||
@@ -203,7 +203,7 @@ int DeLoopQueue(SqQueue &Q, ElemType &x){
|
||||
#### 初始化空队列
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
/*
|
||||
* @Description: 循环队列初始化,队列为空
|
||||
* @Version: Beta1.0
|
||||
@@ -219,7 +219,7 @@ void InitLoopQueque(&Q){
|
||||
|
||||
#### 队列是否为空
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
/*
|
||||
* @Description: 判断循环队列是否为空
|
||||
* @Version: Beta1.0
|
||||
@@ -244,7 +244,7 @@ bool isEmpatyLoopQueue(Q){
|
||||
|
||||
#### 入队操作
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
/*
|
||||
* @Description: 循环队列元素入队
|
||||
* @Version: Beta1.0
|
||||
@@ -276,7 +276,7 @@ bool EnLoopQueue(SqQueue &Q, ElemType x){
|
||||
#### 出队操作
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
/*
|
||||
* @Description: 循环队列元素出队
|
||||
@@ -311,7 +311,7 @@ bool DeLoopQueue(SqQueue &Q, ElemType &x){
|
||||
|
||||
#### 获取队头元素
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
/*
|
||||
* @Description: 获取循环队列队头元素
|
||||
* @Version: Beta1.0
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
队列的链式存储结构:
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
// 链式队列节点定义
|
||||
typedef struct{
|
||||
// 结果点数据域
|
||||
@@ -65,7 +65,7 @@ typedef struct{
|
||||
|
||||
#### 队列初始化
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
/*
|
||||
* @Description: 链式队列初始化
|
||||
@@ -91,7 +91,7 @@ voide InitLinkQueue(LinkQueue &Q){
|
||||
|
||||
#### 判断队空
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
/*
|
||||
* @Description: 判断链式队列是否为空
|
||||
@@ -115,7 +115,7 @@ bool IsEmptyLinkQueue(LinkQueue Q){
|
||||
#### 入队
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
/*
|
||||
* @Description: 链式队列入队操作
|
||||
* @Version: Beta1.0
|
||||
@@ -145,7 +145,7 @@ void EnLinkQueue(LinkQueue &Q, ElemType x){
|
||||
|
||||
#### 出队
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
/*
|
||||
* @Description: 链式队列出队操作
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
经典的斐波拉切数列,可以用递归来实现:
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
// 定义递归函数,实现斐波拉切数列
|
||||
|
||||
@@ -45,7 +45,7 @@ int Fibonacci(n){
|
||||
上面很基础的代码,是分`n=0`和`n=1`的情况,先进行过滤,其他情况下则进行递归,其实在日常开发中,经常会有简化的函数封装
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
// 定义递归函数,实现斐波拉切数列
|
||||
|
||||
@@ -66,7 +66,7 @@ int Fibonacci(n){
|
||||
|
||||
可以采用空间换时间的思路,来降低算法的时间复杂度
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
// 使用非递归实现
|
||||
int Fibonacci(int n) {
|
||||
if(n < 2)
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
<!--
|
||||
* @Description:
|
||||
* @Version: Beta1.0
|
||||
* @Author: 【B站&公众号】Rong姐姐好可爱
|
||||
* @Date: 2021-03-21 23:01:40
|
||||
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
|
||||
* @LastEditTime: 2021-03-21 23:01:40
|
||||
-->
|
||||
|
||||
|
||||
## 基础入门
|
||||
|
||||
|
||||
### 主要内容
|
||||
|
||||
- [基础概念](https://mp.weixin.qq.com/s/M8MOCVnbVxQ3GRuxRaEKIg)
|
||||
- [数据结构三要素](https://mp.weixin.qq.com/s/7sCoHDFtI-Qp0wBcvMH6kQ)
|
||||
- [算法与算法评价](https://mp.weixin.qq.com/s/58fBAwoyg2Ali-HqOJ6t9g)
|
||||
|
||||
|
||||
![]()
|
||||
@@ -1,13 +1,3 @@
|
||||
<!--
|
||||
* @Description: 数据结构——基础入门
|
||||
* @Version: Beta1.0
|
||||
* @Author: 【B站&公众号】Rong姐姐好可爱
|
||||
* @Date: 2021-03-22 07:59:20
|
||||
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
|
||||
* @LastEditTime: 2021-03-22 08:02:01
|
||||
-->
|
||||
|
||||
|
||||
## 基础入门
|
||||
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
### 顺序栈的存储类型
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
// 定义栈中元素的最大个数
|
||||
# define MaxSize 50
|
||||
|
||||
@@ -58,7 +58,7 @@ typedef struct{
|
||||
|
||||
`InitStack(&S)`: 初始化一个空栈`S`,栈顶指针初始化为-1
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
void InitStack(&S){
|
||||
// 栈顶指针-1
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
栈的链式存储类型:
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
// 链栈类型定义【基础】
|
||||
typedef struct LinkNode{
|
||||
@@ -56,7 +56,7 @@ typedef struct LinkStack
|
||||
- 如果链栈存在,进行单链表的结点插入操作,移动指针,结点元素赋值,再将结点压入链栈中,移动链栈栈顶指针,最后链栈元素总数+1,返回true
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
/*
|
||||
* @Description: 基于单链表链栈的进栈操作
|
||||
@@ -94,7 +94,7 @@ bool linkStackPushNode(LinkStack* linkStack,int e){
|
||||
- 如果链栈满足出栈条件,则通过栈顶指针获取到链栈栈底结点,将其数据域赋值给变量e,移动栈顶指针指向待出栈元素的后继结点,同时释放待出栈元素的内存空间,链栈元素总数-1 ,出栈成功,返回true.
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
/*
|
||||
* @Description: 基于单链表链栈的出栈操作
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
队列顺序存储类型:
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
// 队列最大存储元素个数
|
||||
#define MaxSize 50
|
||||
|
||||
@@ -164,7 +164,7 @@ typedef struct {
|
||||
|
||||
|
||||
对应的算法实现:
|
||||
```C++
|
||||
```cpp
|
||||
// 入队算法
|
||||
// 尾插法:Q.data[Q.rear]=x;Q.rear=(Q.rear+1)%Maxsize;Q.tag=1
|
||||
// 队空条件:Q.front== Q.rear且Q.tag==0
|
||||
@@ -204,7 +204,7 @@ int DeLoopQueue(SqQueue &Q, ElemType &x){
|
||||
#### 初始化空队列
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
/*
|
||||
* @Description: 循环队列初始化,队列为空
|
||||
* @Version: Beta1.0
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
队列的链式存储结构:
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
// 链式队列节点定义
|
||||
typedef struct{
|
||||
// 结果点数据域
|
||||
@@ -65,7 +65,7 @@ typedef struct{
|
||||
|
||||
#### 队列初始化
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
/*
|
||||
* @Description: 链式队列初始化
|
||||
@@ -91,7 +91,7 @@ voide InitLinkQueue(LinkQueue &Q){
|
||||
|
||||
#### 判断队空
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
/*
|
||||
* @Description: 判断链式队列是否为空
|
||||
@@ -115,7 +115,7 @@ bool IsEmptyLinkQueue(LinkQueue Q){
|
||||
#### 入队
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
/*
|
||||
* @Description: 链式队列入队操作
|
||||
* @Version: Beta1.0
|
||||
@@ -145,7 +145,7 @@ void EnLinkQueue(LinkQueue &Q, ElemType x){
|
||||
|
||||
#### 出队
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
/*
|
||||
* @Description: 链式队列出队操作
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
经典的斐波拉切数列,可以用递归来实现:
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
// 定义递归函数,实现斐波拉切数列
|
||||
|
||||
@@ -44,7 +44,7 @@ int Fibonacci(n){
|
||||
上面很基础的代码,是分`n=0`和`n=1`的情况,先进行过滤,其他情况下则进行递归,其实在日常开发中,经常会有简化的函数封装
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
// 定义递归函数,实现斐波拉切数列
|
||||
|
||||
@@ -65,7 +65,7 @@ int Fibonacci(n){
|
||||
|
||||
可以采用空间换时间的思路,来降低算法的时间复杂度
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
// 使用非递归实现
|
||||
int Fibonacci(int n) {
|
||||
if(n < 2)
|
||||
|
||||
@@ -103,7 +103,7 @@ L.data=new ElemType[InitSize];
|
||||
|
||||
注意:先判空和临界值,提高算法健壮性
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
/*
|
||||
* @Description: 顺序表的插入操作
|
||||
@@ -166,7 +166,7 @@ bool ListInsert(SqList &L, int i, ElemType e){
|
||||
- 成功,返回true,将被删除的元素用引用变量返回;
|
||||
- 失败,返回false
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
/*
|
||||
* @Description: 顺序表的删除操作
|
||||
@@ -231,7 +231,7 @@ bool ListDelete(SqList &L, int i, ElemType &e){
|
||||
在顺序表L中查找第一个元素值等于e的元素,并返回位序
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
/*
|
||||
* @Description: 顺序表的按值查找(顺序查找)
|
||||
* @Version: Beta1.0
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
|
||||
|
||||
单链表中结点类型的描述:
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
// 单链表结点类型定义
|
||||
typeof struct LNode{
|
||||
@@ -91,7 +91,7 @@ typeof struct LNode{
|
||||
> 从空表开始,生成新的结点,将读取的数据存放在新结点的数据域中,将新结点插入到当前链表的表头【头结点之后】
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
/*
|
||||
* @Description: 单链表头插法创建
|
||||
* @Version: Beta1.0
|
||||
@@ -142,7 +142,7 @@ LinkList CreateListWithStartNode(LinkList &L){
|
||||
>新结点插入到当前链表的表尾上,必须增加一个尾指针r,始终指向当前链表的尾结点;
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
/*
|
||||
* @Description: 单链表尾插法创建
|
||||
@@ -201,7 +201,7 @@ LinkList CreateListWithEndNode(LinkList &L){
|
||||
> 在单链表中从第一个结点出发,顺指针next域逐个往下搜索、遍历,直到找出第i个结点为止,否则返回最后一个结点指针域NULL
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
/*
|
||||
* @Description: 单链表按序号查找
|
||||
@@ -255,7 +255,7 @@ LNode *GetElem(LinkList L,int i){
|
||||
> 从单链表的第一个结点开始,从前往后依次比较表中个结点数据域的值,等于给定值e,则返回该结点的指针;若整个单链表【遍历完】中没有数据域值为e的结点,则返回NULL;
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
/*
|
||||
* @Description: 单链表按值查找
|
||||
@@ -296,7 +296,7 @@ LNode *LocateElem(LinkList L,ElemType e){
|
||||
- 第二步: 找到待插入位置的前驱结点,即第(i-1)个结点;
|
||||
- 第三部: 在前驱结点后插入新结点;
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
// 循环遍历,时间复杂度O(n)
|
||||
p=GetElem(L,i-1);
|
||||
|
||||
@@ -334,7 +334,7 @@ LNode *LocateElem(LinkList L,ElemType e){
|
||||
> 在某结点的后面插入一个新的结点,单链表插入算法中,通常采用后插操作的
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
// 结点s插入到结点p的前面,修改指针域,顺序不能改变
|
||||
s->next=p->next;
|
||||
@@ -361,7 +361,7 @@ s->data=temp;
|
||||
- 第三步: 移动指针,删除结点元素;
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
// 获取删除位置结点元素的前驱结点
|
||||
p=GetElem(L,i-1);
|
||||
@@ -392,7 +392,7 @@ free(q)
|
||||
- 第四步:释放q的内存空间
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
// 存放p的后继结点指针
|
||||
q=p->next;
|
||||
|
||||
@@ -423,7 +423,7 @@ free(q)
|
||||
|
||||
不带头结点的单链表,当表为空时候,需要单独处理;
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
// 不带头结点的单链表L为空,判定条件是L=NULL。
|
||||
if(L===NULL){
|
||||
// 链表为空,表长为0
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
- `next指针域` 指向结点的后继结点
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
// 双链表结点类型
|
||||
typedef struct DNode{
|
||||
@@ -54,7 +54,7 @@ typedef struct DNode{
|
||||
> 在双链表中p所指的结点之后插入结点s
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
// 第一步
|
||||
s->next=p->next;
|
||||
@@ -76,7 +76,7 @@ p->next=s
|
||||
|
||||
> 删除双链表中结点p的后继结点q
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
// 第一步
|
||||
p->next=q->next;
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
`循环单链表`是在单链表的基础上,将最后一个结点(尾结点)的指针由`NULL`改为指向`头结点`,形成`环`。【单链表----->循环单链表】
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
// 双链表结点类型
|
||||
typedef struct DNode{
|
||||
ElemType data; // 结点的数据域
|
||||
@@ -53,7 +53,7 @@ typedef struct DNode{
|
||||
`循环双链表`是在双链表的基础上,将`尾结点`的`next`指针指向`头结点`,将`头结点`的`prior`指针指向`尾结点`。【双链表----->循环双链表】
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
// 双链表结点类型
|
||||
typedef struct DNode{
|
||||
ElemType data; // 结点的数据域
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
静态链表结构类型:
|
||||
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
|
||||
// 定义静态链表的最大长度
|
||||
# define MaxSize 50
|
||||
|
||||
@@ -1,13 +1,3 @@
|
||||
<!--
|
||||
* @Description:
|
||||
* @Version: Beta1.0
|
||||
* @Author: 【B站&公众号】Rong姐姐好可爱
|
||||
* @Date: 2021-03-09 08:12:38
|
||||
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
|
||||
* @LastEditTime: 2021-03-22 08:01:49
|
||||
-->
|
||||
|
||||
|
||||
## 线性表
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user