栈、队列小组讨论
集美大学专题讨论1:栈、队列
| 项目名称 | 内容 |
|---|---|
| 课程名称 | 数据结构 |
| 班级 | 网安2511、2512 |
| 学号 | 202521336035 |
| 学号 | 202521336002 |
| 学号 | 202521336001 |
| 专题讨论名称 | 使用费曼学习法学习栈与队列的应用 |
一、目的(本次实验所涉及并要求掌握的知识点)
- 深入理解栈与队列的核心特性。
- 掌握深度优先搜索(DFS)与广度优先搜索(BFS)的基本思想。
- 熟练实现栈与队列的非递归应用案例。
- 运用费曼学习法深化理解。
- 培养团队协作与专题总结能力。
二、实验内容与设计思想
题目1:使用栈走迷宫
函数相关伪代码
栈迷宫搜索函数search(mg迷宫二维数组,sx,sy起点横纵坐标,ex,ey终点横纵坐标)
h = mg.size()
l = mg[0].size()
stack<point>st 初始化空栈
st.push(point(sx, sy)) 起点入栈
mg[sx][sy] = 2 标记已走路径
while (!st.empty()) 栈非空则循环探索
point& cur = st.top() 取栈顶元素
x = cur.x
y = cur.y
if (x == ex && y == ey)
return true 到达终点,返回成功
bool next = false 标记是否找到可走方向
for (int i = cur.dir+1; i < 4; ++i) 从下一个方向开始遍历
nx = x + dx[i]
ny = y + dy[i]
if (nx >= 0 && nx < h && ny >= 0 && ny < l && mg[nx][ny] == 0) 是否不越界+可走通
cur.dir = i 更新尝试方向
st.push(point(nx, ny)) 新坐标入栈
mg[nx][ny] = 2 标记路径
next = true
break
if (!next) 无可走方向,弹出栈顶
st.pop()
return false
打印迷宫函数printmg(mg)
for (auto& hh : mg) 遍历数组
for (int v :hh)
if (v == 1)cout << "#" 输出墙
else if (v == 2)cout << "*" 输出路径
else cout << " " 输出通路
cout << endl
函数代码
#include<iostream>
#include<stack>
#include<vector>
using namespace std;
struct point {
int x, y;
int dir;
point(int x_,int y_,int d_=-1):x(x_),y(y_),dir(d_){}
};
int dx[] = { -1,1,0,0 };
int dy[] = { 0,0,-1,1 };
bool search(vector<vector<int>>& mg, int sx, int sy, int ex, int ey) {
int h = mg.size();
int l = mg[0].size();
stack<point>st;
st.push(point(sx, sy));
mg[sx][sy] = 2;
while (!st.empty()) {
point& cur = st.top();
int x = cur.x;
int y = cur.y;
if (x == ex && y == ey) {
return true;
}
bool next = false;
for (int i = cur.dir+1; i < 4; ++i) {
int nx = x + dx[i];
int ny = y + dy[i];
if (nx >= 0 && nx < h && ny >= 0 && ny < l && mg[nx][ny] == 0) {
cur.dir = i;
st.push(point(nx, ny));
mg[nx][ny] = 2;
next = true;
break;
}
}
if (!next) {
st.pop();
}
}
return false;
}
void printmg(vector<vector<int>>& mg) {
for (auto& hh : mg) {
for (int v :hh) {
if (v == 1)cout << "#";
else if (v == 2)cout << "*";
else cout << " ";
}
cout << endl;
}
}
int main(){
vector<vector<int>>mg = {
{1,1,1,1,1,1,1},
{1,0,0,1,1,0,1},
{1,0,1,1,0,0,1},
{1,0,1,1,1,0,1},
{1,0,0,0,0,0,1},
{1,1,1,1,1,0,1},
{1,1,1,1,1,1,1}
};
int sx = 1, sy = 1;
int ex = 5, ey = 5;
cout << "原始迷宫:" << endl;
printmg(mg);
if (search(mg, sx, sy, ex, ey)) {
cout << "栈探索完毕,找到路径:" << endl;
printmg(mg);
}
else {
cout << "无可达路径" << endl;
}
return 0;
}
题目2:使用队列走迷宫
函数相关伪代码
函数 BFS_迷宫求解(迷宫, 起点, 终点):
初始化队列 q
初始化 visited 数组标记已访问位置
初始化 prev 数组记录路径
将起点加入队列
标记起点为已访问
while 队列不为空:
当前点 = 队列出队
if 当前点 == 终点:
从prev数组回溯构建路径
返回路径
遍历四个方向(上,下,左,右):
计算下一个点
if 下一个点在迷宫范围内 且 不是墙 且 未访问过:
标记为已访问
记录前驱节点
将下一个点加入队列
返回 无路径
函数代码
#include <iostream>
#include <vector>
#include <queue>
#include <stack>
using namespace std;
// 方向:上、右、下、左
const int dx[4] = {-1, 0, 1, 0};
const int dy[4] = {0, 1, 0, -1};
struct Point {
int x, y;
Point(int x = 0, int y = 0) : x(x), y(y) {}
bool operator==(const Point& other) const {
return x == other.x && y == other.y;
}
};
// 使用BFS在迷宫中寻找最短路径
vector<Point> bfsMazeSolver(vector<vector<int>>& maze, Point start, Point end) {
int rows = maze.size();
int cols = maze[0].size();
// 方向名称,用于输出路径方向
vector<string> dirName = {"上", "右", "下", "左"};
// 访问标记数组
vector<vector<bool>> visited(rows, vector<bool>(cols, false));
// 前驱节点数组,记录每个点的前一个点
vector<vector<Point>> prev(rows, vector<Point>(cols, Point(-1, -1)));
// 前驱方向数组,记录到达当前点的方向
vector<vector<int>> prevDir(rows, vector<int>(cols, -1));
queue<Point> q;
// 起点入队
q.push(start);
visited[start.x][start.y] = true;
while (!q.empty()) {
Point current = q.front();
q.pop();
// 如果到达终点
if (current.x == end.x && current.y == end.y) {
// 回溯构建路径
vector<Point> path;
stack<Point> pathStack;
vector<string> pathDirections;
Point p = end;
while (!(p == start)) {
pathStack.push(p);
int dir = prevDir[p.x][p.y];
if (dir != -1) {
pathDirections.push_back(dirName[dir]);
}
p = prev[p.x][p.y];
}
pathStack.push(start);
// 将路径从栈中取出,得到从起点到终点的顺序
while (!pathStack.empty()) {
path.push_back(pathStack.top());
pathStack.pop();
}
// 输出路径方向
cout << "路径方向(从起点到终点):";
for (int i = pathDirections.size() - 1; i >= 0; i--) {
cout << pathDirections[i];
if (i > 0) cout << "->";
}
cout << endl;
return path;
}
// 探索四个方向
for (int i = 0; i < 4; i++) {
int nx = current.x + dx[i];
int ny = current.y + dy[i];
// 检查是否在迷宫范围内、不是墙、且未访问
if (nx >= 0 && nx < rows && ny >= 0 && ny < cols &&
maze[nx][ny] == 0 && !visited[nx][ny]) {
visited[nx][ny] = true;
prev[nx][ny] = current;
prevDir[nx][ny] = i; // 记录从哪个方向到达这个点
q.push(Point(nx, ny));
}
}
}
// 没有找到路径
return vector<Point>();
}
// 打印迷宫
void printMaze(const vector<vector<int>>& maze,
const vector<Point>& path = vector<Point>()) {
int rows = maze.size();
int cols = maze[0].size();
// 创建标记数组
vector<vector<bool>> isPath(rows, vector<bool>(cols, false));
for (const auto& p : path) {
isPath[p.x][p.y] = true;
}
cout << "迷宫地图 (0=通路, 1=墙, S=起点, E=终点, *=路径):" << endl;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if (maze[i][j] == 1) {
cout << "██"; // 墙
} else if (i == 0 && j == 0) {
cout << "S "; // 起点
} else if (i == rows-1 && j == cols-1) {
cout << "E "; // 终点
} else if (isPath[i][j]) {
cout << "* "; // 路径
} else {
cout << " "; // 通路
}
}
cout << endl;
}
}
// 打印路径坐标
void printPath(const vector<Point>& path) {
if (path.empty()) {
cout << "没有找到路径!" << endl;
return;
}
cout << "找到路径,共" << path.size() << "步:" << endl;
for (size_t i = 0; i < path.size(); i++) {
cout << "步骤" << i << ": (" << path[i].x << ", " << path[i].y << ")";
if (i < path.size() - 1) {
cout << " -> ";
}
if ((i + 1) % 3 == 0) cout << endl;
}
cout << endl;
}
int main() {
// 示例迷宫:0表示通路,1表示墙
vector<vector<int>> maze = {
{0, 1, 0, 0, 0},
{0, 1, 0, 1, 0},
{0, 0, 0, 0, 0},
{0, 1, 1, 1, 0},
{0, 0, 0, 1, 0}
};
// 起点和终点
Point start(0, 0);
Point end(4, 4);
cout << "=== 使用队列(BFS)走迷宫 ===" << endl;
// 打印原始迷宫
cout << "\n原始迷宫:" << endl;
printMaze(maze);
// 使用BFS寻找路径
vector<Point> path = bfsMazeSolver(maze, start, end);
// 打印路径
printPath(path);
// 打印带路径的迷宫
if (!path.empty()) {
cout << "\n带路径的迷宫:" << endl;
printMaze(maze, path);
}
return 0;
}
题目3:使用队列解决舞伴问题
函数相关伪代码
函数 QueueLen(Q)
// Q 是一个循环队列
返回 (Q.rear - Q.front + MAXQSIZE) mod MAXQSIZE
结束
函数 EnQueue(Q, e)
// 判断队列是否已满
如果 (Q.rear + 1) mod MAXQSIZE == Q.front
返回 ERROR
否则
将 e 存入 Q.data[Q.rear]
Q.rear = (Q.rear + 1) mod MAXQSIZE
返回 OK
结束
函数 QueueEmpty(Q)
如果 Q.front == Q.rear
返回 OK // 表示空
否则
返回 ERROR
结束
函数 DeQueue(Q, &e)
// 判断队列是否为空
如果 Q.front == Q.rear
返回 ERROR
否则
e = Q.data[Q.front]
Q.front = (Q.front + 1) mod MAXQSIZE
返回 OK
结束
函数 DancePartner(dancer[], num)
// 男女分别入队
对于 i 从 0 到 num-1
如果 dancer[i].sex == 'F'
EnQueue(Fdancers, dancer[i])
否则
EnQueue(Mdancers, dancer[i])
// 依次配对
当 Fdancers 非空 且 Mdancers 非空
从 Fdancers 出队一个人,输出姓名
从 Mdancers 出队一个人,输出姓名
结束
函数代码
#include<iostream>
#define MAXQSIZE 100 // 队列最大长度
#define OK 1
#define ERROR 0
#define OVERFLOW -2
using namespace std;
// 人员结构体
typedef struct {
char name[20]; // 姓名
char sex; // 性别 F-女 M-男
} Person;
// 队列的顺序存储结构(循环队列)
typedef struct {
Person data[MAXQSIZE]; // 存储队列元素
int front; // 队头指针
int rear; // 队尾指针
} Queue;
typedef Queue* SqQueue;
SqQueue Mdancers, Fdancers; // 男士队列、女士队列
// 函数声明
int InitQueue(SqQueue& Q);
void DestroyQueue(SqQueue& q);
int QueueLen(SqQueue Q);
int EnQueue(SqQueue& Q, Person e);
int QueueEmpty(SqQueue& Q);
int DeQueue(SqQueue& Q, Person& e);
void DancePartner(Person dancer[], int num);
int main() {
int i;
int n;
Person dancer[MAXQSIZE];
// 输入人数与人员信息
cin >> n;
for (i = 0; i < n; i++) {
cin >> dancer[i].name >> dancer[i].sex;
}
// 队列初始化
InitQueue(Mdancers);
InitQueue(Fdancers);
cout << "The dancing partners are:" << endl;
// 舞伴配对
DancePartner(dancer, n);
// 输出剩余等待人员
if (!QueueEmpty(Fdancers)) {
cout << "F:" << QueueLen(Fdancers);
}
else if (!QueueEmpty(Mdancers)) {
cout << "M:" << QueueLen(Mdancers);
}
// 销毁队列,释放内存
DestroyQueue(Fdancers);
DestroyQueue(Mdancers);
return 0;
}
// 初始化循环队列
int InitQueue(SqQueue& Q) {
Q = new Queue; // 分配队列空间
if (!Q) { // 分配失败判断
exit(OVERFLOW);
}
Q->front = Q->rear = 0; // 队头队尾置0,队列为空
return OK;
}
// 销毁队列
void DestroyQueue(SqQueue& q) {
delete q;
q = NULL;
}
// 求队列长度
int QueueLen(SqQueue Q) {
return (Q->rear - Q->front + MAXQSIZE) % MAXQSIZE;
}
// 入队操作
int EnQueue(SqQueue& Q, Person e) {
// 判断队列是否满
if ((Q->rear + 1) % MAXQSIZE == Q->front) {
return ERROR;
}
Q->data[Q->rear] = e;
Q->rear = (Q->rear + 1) % MAXQSIZE; // 循环移动队尾指针
return OK;
}
// 判断队列是否为空
int QueueEmpty(SqQueue& Q) {
if (Q->rear == Q->front) {
return OK;
}
return ERROR;
}
// 出队操作
int DeQueue(SqQueue& Q, Person& e) {
// 判断队列是否空
if (Q->rear == Q->front) {
return ERROR;
}
e = Q->data[Q->front]; // 取出队头元素
Q->front = (Q->front + 1) % MAXQSIZE;// 循环移动队头指针
return OK;
}
// 舞伴配对核心函数
void DancePartner(Person dancer[], int num) {
// 第一步:男女分队列
for (int i = 0; i < num; ++i) {
if (dancer[i].sex == 'F') {
EnQueue(Fdancers, dancer[i]);
}
else {
EnQueue(Mdancers, dancer[i]);
}
}
Person e;
// 第二步:男女依次出队配对
while (!QueueEmpty(Fdancers) && !QueueEmpty(Mdancers)) {
DeQueue(Fdancers, e);
cout << e.name << " ";
DeQueue(Mdancers, e);
cout << e.name << endl;
}
}
三、实验使用环境(本次实验所使用的平台和相关软件)
以下请根据实际情况编写
- 操作系统:Windows 10
- 编程语言:C++
- 开发工具:Visual Studio 2022
- 编译器:C++
四、实验步骤和调试过程(实验步骤、测试数据设计、测试结果分析)
题目1:使用栈走迷宫
本机运行截图

题目2:使用队列走迷宫
本机运行截图

题目3:使用队列解决舞伴问题
本机运行截图

五、讨论与提问过程(总结性议题)
- 为什么栈走迷宫不是最短路径,而队列是?
答:栈是深度优先,一条路走到黑,第一条找到的是先探索的路,不一定最短;队列是广度优先,一层一层扩散,第一次到终点的就是步数最少的最短路径。 - 栈和队列的核心区别是什么?
答:栈是后进先出,用于回溯;队列是先进先出,用于顺序处理。 - 舞伴问题为什么用队列?
答:舞伴问题需要先来先配对的顺序,完全符合队列先进先出的特性。 - 迷宫走法中,如何避免重复走?
答:用标记数组,将把走过的路设为墙,或者访问数组,避免重复入栈/入队,导致死循环。
六、实验小结(实验体会和收获)
实验体会和收获:
- 深刻理解了栈 “后进先出” 与队列 “先进先出” 的本质差异。
- 掌握了 DFS 与 BFS 的核心思想与实现差异。
- 提升了代码实现与调试能力。
- 体会到数据结构对程序效率与功能的重要影响。
- 增强了团队协作与表达能力。
六、附件(参考文献和相关资料)
以下请根据实际情况编写

浙公网安备 33010602011771号