1
1
mirror of https://github.com/ParkMoonJ/KaoYan.git synced 2026-11-04 14:54:43 +08:00

Create 3 栈和队列.md

This commit is contained in:
ParkMoonJ
2021-04-08 14:58:12 +08:00
parent 654ba2927c
commit e2653a97d2

View File

@@ -0,0 +1,205 @@
# 第 3 章 栈和队列
## 一、基本内容
栈和队列的结构特性;在两种存储结构上如何实现栈和队列的基本操作以及栈和队列在程序设计中的应用。
## 二、学习要点
1. 掌握栈和队列这两种抽象数据类型的特点,并能在相应的应用问题中正确选用它们。
2. 熟练掌握栈类型的两种实现方法,即两种存储结构表示时的基本操作实现算法,特别应注意栈满和栈空的条件以及它们的描述方法。
3. 熟练掌握循环队列和链队列的基本操作实现算法,特别注意队满和队空的描述方法。
4. 理解递归算法执行过程中栈的状态变化过程。
5. 理解递归算法到非递归算法的机械转化过程。
其中,后两点属于高难度的学习内容,在标号左上角加上双重星号(**),以示区别。
这一章的习题明显地可看出有三类:第一类涉及栈的类型特点及其应用,如题 3.1 至题 3.10,题 3.15 至 3.23;第二类涉及递归算法执行过程中栈的状态和递归的消除,如题 3.24 至题 3.27 ;第三类涉及队列的类型特点和应用以及在不同存储结构上的实现方法,如题 3.12 至 3.14 和 3.28 至题 3.34。
## 三、算法演示内容
在 DSDEMO 系统的选单「栈」下,有以下算法的演示可供学习时参考:
- 递归算法
- 汉诺塔(Hanoi)
- 迷宫(Maze)
- 皇后问题(Queen)
- 背包问题:求得一组解(Knap)和求得全部解(Bag);
- 计算阿克曼函数(Ack);
- 利用栈进行车辆调度求得出站车厢序列(Gen,Perform);
- 表达式求值(Exp_reduced);
- 离散事件模拟(Bank_Simulation)。
## 四、基础知识题
### 3.1 若按教科书 3.1.1 节中图 3.1(b) 所示铁道进行车厢调度(注意:两侧铁道均为单向行驶道),则请回答:
#### (1)如果进站的车厢序列为 123,则可能得到的出站车厢序列是什么?
```
>> SXSXSX
>> 1S - 1X - 2S - 2X - 3S - 3X
<< 123
>> SXSSXX
>> 1S - 1X - 2S - 3S - 3X - 2X
<< 132
>> SSXXSX
>> 1S - 2S - 2X - 1X - 3S - 3X
<< 213
>> SSXSXX
>> 1S - 2S - 2X - 3S - 3X - 1X
<< 231
>> SSSXXX
>> 1S - 2S - 3S - 3X - 2X - 1X
<< 321
```
#### (2)如果进站的车厢序列为 123456,则能否得到 435612 和 135426 的出站序列,并请说明为什么不能得到或者如何得到(即写出以「S」表示进栈和以「X」表示出栈的栈操作序列)。
```
>> 1S - 2S - 3S - 4S - 4X - 3X - 5S - 5X - 6S - 6X - 2X - 1X
// SSSSXXSXSXXX
<< 435621
// 不能得到 435612
>> 1S - 1X - 2S - 3S - 3X - 4S - 5S - 5X - 4X - 2X - 6S - 6X
// SXSSXSSXXXSX
<< 135426
// 可以得到 135426
```
### 3.2 简述栈和线性表的差别。
从数据结构角度看,栈也是线性表,其特殊性在于栈的基本操作是线性表操作的子集,它是操作受限的线性表,因此,可称为限定性数据结构。但从数据类型角度看,它是和线性表大不相同的重要的抽象数据类型。
栈是限定在表尾进行插入或删除操作的线性表。栈又称为 **后进先出(last in first out)** 的线性表(简称 **LIFO 结构**)。
### 3.3 写出下列程序段的输出结果(栈的元素类型 `SElemType` 为 `char`)。
```cpp
void main() {
Stack S;
char x,y;
InitStack(S);
x = 'c';
y = 'k';
Push(S, x); // S = {'c'}
Push(S, 'a'); // S = {'c', 'a'}
Push(S, y); // S = {'c', 'a', 'k'}
Pop(S, x); // S = {'c', 'a'}; x = 'k'
Push(S, 't'); // S = {'c', 'a', 't'}
Push(S, x); // S = {'c', 'a', 't', 'k'}
Pop(S, x); // S = {'c', 'a', 't'}; x = 'k'
Push(S, 's'); // S = {'c', 'a', 't', 's'}
while (!StackEmpty(S)) {
Pop(S, y);
printf(y);
}
printf(x);
}
```
```cpp
<< stack
```
### 3.4 简述以下算法的功能(栈的元素类型 `SElemType` 为 `int`)。
#### (1)
```cpp
Status algo1(Stack S) {
int i, n, A[255];
n = 0;
while (!StackEmpty(S)) {
n++;
Pop(S, A[n]); // 依序出栈,存入数组
}
for (i = 1; i <= n; i++) {
Push(S, A[i]); // 除原栈顶元素外,依次入栈
}
} // 除原栈顶元素外,栈内元素倒序排列
```
#### (2)
```cpp
Status algo2(Stack S, int e) {
Stack T;
int d;
InitStack(T);
while (!StackEmpty(S)) {
Pop(S, d); // 依序出栈 S
if (d != e) { // 判断出栈元素是否等于 e
Push(T, d); // 若非 e 依序进栈 T
}
}
while (!StackEmpty(T)) {
Pop(T, d); // 依序出栈 T
Push(S, d); // 依序进栈 S
}
} // 删除栈内所有值等于 e 的元素
```
### 3.5 假设以 S 和 X 分别表示入栈和出栈的操作,则初态和终态均为栈空的入栈和出栈的操作序列可以表示为仅由 S 和 X 组成的序列。称可以操作的序列为合法序列(例如,SXSX 为合法序列,SXXS 为非法序列)。试给出区分给定序列为合法序列或非法序列的一般准则,并证明:两个不同的合法(栈操作)序列(对同一输入序列)不可能得到相同的输出元素(注意:在此指的是元素实体,而不是值)序列。
### 3.6 试证明:若借助栈由输入序列 $1 2 \cdots n$得到的输出序列为 $p_1 p_2 \cdots p_n$,(它是输入序列的一个排列),则在输出序列中不可能出现这样的情形:存在着 $i < j < k$ 使 $p_j < p_k < p_i$。
### 3.7 按照四则运算加、减、乘、除和幂运算(^)优先关系的惯例,并仿照教科书 3.2 节例 3-2 的格式,画出对下列算术表达式求值时操作数栈和运算符栈的变化过程:
$$
A - B \times C / D + E ^ F
$$
### 3.8 试推导求解 $n$ 阶梵塔问题至少要执行的 `move` 操作的次数。
### 3.9 试将下列递推过程改写为递归过程。
```cpp
void ditui(int n) {
int i;
i = n;
while (i > 1) {
printf(i--);
}
}
```
### 3.10 试将下列递归过程改写为非递归过程。
```cpp
void test(int &sum) {
int x;
scanf(x);
if(x == 0) {
sum = 0;
} else {
test(sum);
sum += x;
}
printf(sum);
}
```
### 3.11 简述队列和栈这两种数据类型的相同点和差异处。
### 3.12 写出以下程序段的输出结果(队列中的元素类型 `QElemType` 为 `char`)。
```cpp
void main() {
Queue Q;
InitQueue(Q);
char x = 'e', y = 'c';
EnQueue(Q, 'h');
EnQueue(Q, 'r');
EnQueue(Q, y);
DeQueue(Q, x);
EnQueue(Q, x);
DeQueue(Q, x);
EnQueue(Q, 'a');
while(!QueueEmpty(Q)) {
```