InkSoul/content/408/《数据结构》栈、队列和数组.md

486 lines
11 KiB
Markdown
Raw Permalink Normal View History

2023-07-30 17:05:22 +08:00
---
title: "《数据结构》栈、队列和数组"
date: 2023-07-30T14:53:00+08:00
---
## 栈
### 逻辑结构知识点
|栈|只允许在一段进行插入或删除操作的线性表|
|---|---|
|栈顶|线性表允许进行插入删除的那一段|
|栈底|线性表允许进行插入删除的那一段|
|操作特性|先进后出|
|数学性质|n个不同元素进栈出栈元素的不同排列的个数为$\frac{1}{n+1}C^n_{2n}$(卡特兰数)
### 存储结构知识点
#### 栈的顺序存储结构
##### 顺序栈的定义
* 采用顺序存储的栈
* 利用一组地址连续的存储单元存放字栈底到栈顶的数据元素
##### 顺序栈的实现
```c
#define Maxsize 50
typedef struct
{
Elemtype data[Maxsize];//存放栈顶元素
int top;//栈顶指针
}SqStack;
```
|栈顶指针|S.top,初始时设置S.top = -1;栈顶元素S.data[S.top]|
|---|---|
|进栈操作|栈不满时栈顶指针先加1再送值到栈顶元素|
|出栈操作|栈非空时先找栈顶元素值再将栈顶指针减1|
|栈空条件|S.top == -1|
|栈满条件|S.top == Maxsize-1;栈长S.top+1|
##### 顺序栈基本运算代码
初始化
```c
Void InitStack(SqStack &S)
{
S.top = -1;
}
```
----
判栈空
```c
bool StackEmpty(SqStack S)
{
if (S.top == -1)//栈空
return true;
else
return false;
}
```
---
进栈
```c
bool Push(SqStack &S,ElemType x)
{
if(S.top == Maxsize -1)//栈满
return false;
S.data[++S.top] = x ;
return true;
}
```
---
出栈
```c
bool Pop(SqStack &S,ElemType x)
{
if(S.top == -1)//栈空
return false;
S.data[S.top--] = x;
return true;
}
```
---
读栈顶元素
```c
bool GetTop(SqStack S,ElemType x)
{
if(S.top==-1)//栈空
return false;
x = S.data[S.top]
return true;
}
```
##### 共享栈
* 定义
* 利用栈底位置相对不变的特性,可让两个顺序栈共享一个一维数组空间
* 将两个栈的栈底分别设置在共享空间的两端,两个栈顶向共享空间的中间延伸
* 特点
* ![](../../images/408/《数据结构》栈、队列和数组/共享栈特点.jpg)
2023-07-30 17:05:22 +08:00
* 目的
* 更有效地利用存储空间
* 两个栈的空间相互调节,只有在整个存储空间都被占满时才发生上溢
##### 栈的链式存储结构
* 概念
* 采用链式存储的栈
* 优点
* 便于多个栈共享空间和提高其效率,且不存在栈满上溢的情况
* 特点
* 通常采用单链表实现,并规定所有操作都在单链表的表头进行的
* 规定链栈没有头结点Lhead指向栈顶元素
链式栈实现
```c
typedef struct Linknode
{
Elemtype data;//存放栈中元素
struct Linknode *next;//栈顶指针
}* LiStack;
```
### 栈的运算/操作
|InitStack(&S)|初始化栈|初始化一个空栈S|
|---|---|---|
|StackEmpty(S)|判断栈是否为空|空则返回True|
|Push(&S,x)|进栈|若S未满则将x加入使之成为新栈顶|
|Pop(&S,&x)|出栈|若S非空则弹出栈顶元素并用x返回|
|GetTop(S,&x)|读栈顶元素|若栈非空则用x返回栈顶元素|
|DestroyStack(&S)|销毁栈|销毁并释放栈S占用的存储空间|
### 栈的应用
#### 括号匹配
* 初始设置一个空栈,顺序读入括号
* 若是右括号,置于栈顶
* 若是左括号,压入栈中
* 算法结束时,栈为空,否则括号序列不匹配
#### 表达式求值
后缀表达式和正常表达式的相互转换
![](../../images/408/《数据结构》栈、队列和数组/后缀表达式和正常表达式的相互转换.jpg)
2023-07-30 17:05:22 +08:00
---
中缀到后缀表达式转换的过程
![](../../images/408/《数据结构》栈、队列和数组/中缀到后缀表达式转换的过程.jpg)
2023-07-30 17:05:22 +08:00
#### 递归
* 在递归调用的过程,系统为每一层的返回点、局部变量、传入实参等开辟了递归工作栈来进行数据存储
* 递归次数过多容易造成栈溢出
* 将递归算法转换为非递归算法,通常需要借助栈来实现这种转换,消除递归不一定需要栈
* 效率不高,原因是递归调用过程中包含很多重复的计算
* 代码简单,容易理解
## 队列
### 逻辑结构知识点
|队列|操作受限的线性表|
|---|---|
|队头|允许删除的一端|
|队尾|允许插入的一端|
|操作特性|先进先出|
### 存储结构知识点
#### 队列的顺序存储结构
##### 顺序队列的定义
* 分配一块连续的存储单元存放队列中的元素
* 设两个指针
* 队头指针front指向队头元素
* 队尾指针rear指向队尾元素的下一个位置
##### 顺序队列的实现
![](../../images/408/《数据结构》栈、队列和数组/顺序队列的实现.jpg)
2023-07-30 17:05:22 +08:00
```c
#define MaxSize 50
typedef struct
{
ElemType data[MaxSize];
int front,rear;
}SqQueue;
```
|初始状态|Q.front ==Q.rear = 0|
|---|---|
|队满操作|Q.rear == MaxSize不能作为队列满的条件<br>只有一个元素仍满足该条件(假溢出)|
|进队操作|队不满时先送值到队尾元素再将队尾指针加1|
|出队操作|队不空时先取队头元素值再将队头指针加1|
##### 循环队列的定义
* 把存储队列元素的表从逻辑上视为一个环
##### 循环队列的实现
![](../../images/408/《数据结构》栈、队列和数组/循环队列的实现.jpg)
2023-07-30 17:05:22 +08:00
##### 区分循环队列队空还是队满
||方法一|方法二|方法三|
|---|---|---|---|
|定义|牺牲一个单元来区分队空和队满,入队时少用一个队列单元,约定以队头指针在队尾指针的下一位置为作为队满的标志|类型中增设表示元素个数的数据成员|类型中增设tag数据成员以区分是队满还是队空|
|队空条件|Q.front == Q.rear|Q.size==0|tag=0且因删除导致Q.front == Q.rear|
|队满条件|(Q.rear+1)%MaxSize == Q.front|Q.size == MaxSize|tag=0且因插入导致Q.front==Q.rear|
|元素个数|(Q.rear - Q.front + MaxSize)%MaxSize|||
##### 循环队列的操作代码
初始化
```c
void InitQueue(SqQueue &Q)
{
Q.rear = Q.front =0;
}
```
---
判队空
```c
bool isEmpty(SqQueue Q)
{
if(Q.rear == Q.front)
return true;
else
return false;
}
```
---
入队
```c
bool EnEmpty(SqQueue &Q,ElemType x)
{
if ((Q.rear + 1)% MaxSize == Q.front)
return false;
Q.data[Q.rear] = x;
Q.rear = (Q.rear + 1) % MaxSize;
return true;
}
```
---
出队
```c
bool DeEmpty(SqQueue &Q,ElemType &x)
{
if(Q.rear == Q.front)
return false;
x = Q.data[Q.front];
Q.front = (Q.font + 1) % MaxSize;
return true;
}
```
#### 队列的链式存储结构
##### 链式队列的定义
* 实质是一个同时有队头指针和队尾指针的单链表
* 头指针指向队头结点,尾指针指向队尾结点,即单链表的最后一个节点
* 删除操作时,通常仅需要修改头指针
* 当队列只有一个元素时删除后队列为空修改尾指针为rear = front
##### 链式队列的实现
```c
typedef struct LinkNode
{
ElemType data;
struct LinkNode *next;
}LinkNode;
typedef struct
{
LinkNode * front,*rear;
}LinkNode;
```
##### 链式队列的操作代码
初始化
```c
void InitQueue(LinkQueue &Q)
{
Q.front = Q.rear = (LinkNode *) malloc(sizeof(LinkNode));
Q.front ->next = NULL;
}
```
---
判队空
```c
bool isEmpty(LinkQueue Q)
{
if(Q.rear == Q.front)
return true;
else
return false;
}
```
---
入队
```c
bool EnEmpty(LinkQueue &Q,ElemType x)
{
LinkNode *s = (LinkNode * )malloc(sizeof(LinkNode));
s->data =x;
s->next = NULL;
Q.rear->next =s;
Q.rear = s;
}
```
---
出错
```c
bool DeEmpty(LinkQueue &Q,ElemType &x)
{
if(Q.rear == Q.front)
return false;
LinkNode *p = Q.front->next;
x = p->data;
Q.front->next = p->next;
if(Q.rear == p)
Q.rear = Q.front;
free(p);
return true;
}
```
##### 双端队列
* 定义
* 允许两端都可以进行入队和出队操作的队列
* 将队列的两端分别称为前端和后端
* 特点
* 其元素的逻辑结构仍是线性结构
* 分类
* 输出受限的双端队列
* 允许在一段进行插入和删除,但在另一端只允许插入的双端队列
* 输出固定,看输入
* 输入受限的双端队列
* 允许在一段进行插入和删除,但在另一端只允许删除的双端队列
* 输入固定,看输出
### 队列的运算/操作
|InitQueue(&Q)|初始化队列|构造一个空队列Q|
|---|---|---|
|QueueEmpty(Q)|判队列为空|空则返回True|
|EnQueue(&Q,x)|入队|若队列未满将x加入使之成为新的队尾|
|DeQueue(&Q,&x)|出队|若队列非空删除表头元素并用x返回|
|GetHead(Q,&x)|读队头元素|若队列非空则将队头元素赋值给x|
### 队列的应用
#### 层次遍历
使用队列是为了保存下一步的处理顺序
![](../../images/408/《计网》应用层/层次域名空间.png)
2023-07-30 17:05:22 +08:00
#### 计算机系统中的应用
* 解决主机与外部设备之间速度不匹配的问题
* 解决由多用户引起的资源竞争问题
* 页面替换算法FIFO算法
## 数组和特殊矩阵
* 重点
* 如何将矩阵更有效地存储在内存中,并能方便地提取矩阵的元素
* 定义
* 由n个相同类型的数据元素构成的有限序列
* 特点
* 数组一旦被定义,其维数和维界就不再改变
* 除结构的初始化和销毁外,数组只会由存取元素和修改元素的操作
* 数组的数据结构
* 一个数组的所有元素在内存中占用一段连续的存储空间
* 多维数组有两种映射方法
* 按行优先
* 先行后列,先存储行号较小的元素,行号相等先存储列好较小的元素
* 按列优先
* 先列后行,先存储列号较小的元素,列号相等先存储行号较小的元素
* 特殊矩阵相关概念
* 压缩存储
* 为多个值相同的元素只分配一个空间,对零元素不分配存储空间,其目的是节省存储空间
* 特殊矩阵
* 具有许多相同矩阵元素或者零元素,并且这些相同矩阵元素或零元素的分布有一定规律性的矩阵
* 常见的特殊矩阵有对称矩阵、三角矩阵第一行和最后一行两个元素其余行3个元素、对角矩阵
* 常考某个值对应的行和列(特殊法代指就好,可用线代知识解决)
* 特殊矩阵的压缩存储方法
* 找到特殊矩阵中值相同的矩阵元素的分布规律
* 把这些呈现规律性分布的、值相同的多个矩阵元素压缩存储到一个存储空间中
* 稀疏矩阵的定义
* 非0元素非常少的矩阵
* 稀疏矩阵的特点
* 稀疏矩阵压缩存储后便失去了随机存取特性
* 可以使用三元组表和十字链表法压缩存储稀疏矩阵