栈、队列小组讨论

集美大学专题讨论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:使用栈走迷宫

本机运行截图
image

题目2:使用队列走迷宫

本机运行截图
ec8c753f277dcda9e293dc4380a054a0

题目3:使用队列解决舞伴问题

本机运行截图
ebb8fd21b323eecc222f2127bea85c16


五、讨论与提问过程(总结性议题)

  • 为什么栈走迷宫不是最短路径,而队列是?
    答:栈是深度优先,一条路走到黑,第一条找到的是先探索的路,不一定最短;队列是广度优先,一层一层扩散,第一次到终点的就是步数最少的最短路径。
  • 栈和队列的核心区别是什么?
    答:栈是后进先出,用于回溯;队列是先进先出,用于顺序处理。
  • 舞伴问题为什么用队列?
    答:舞伴问题需要先来先配对的顺序,完全符合队列先进先出的特性。
  • 迷宫走法中,如何避免重复走?
    答:用标记数组,将把走过的路设为墙,或者访问数组,避免重复入栈/入队,导致死循环。

六、实验小结(实验体会和收获)

实验体会和收获:

  • 深刻理解了栈 “后进先出” 与队列 “先进先出” 的本质差异。
  • 掌握了 DFS 与 BFS 的核心思想与实现差异。
  • 提升了代码实现与调试能力。
  • 体会到数据结构对程序效率与功能的重要影响。
  • 增强了团队协作与表达能力。

六、附件(参考文献和相关资料)

以下请根据实际情况编写

  1. C++ Primer
  2. 实验3-栈与队列
posted @ 2026-04-09 12:42  xin_fffffly  阅读(13)  评论(0)    收藏  举报