迷宫问题(BFS, DFS)

The Core Thoughts

核心思想

  • DFS (Depth First Search):
  • 深度优先搜索

  Save the path that can go through using Stack (if it can go through at present, push it to the Stack). When there is no way to go, then go back to the path that has not yet been searched through backtracking. If there is no result when backtracking until the Stack is empty, there is no solution.

  使用栈保存可以通过的路径(只要如果目前可以通过,则将该位置压栈)。 当无路可走时,再通过回溯回到尚未搜索到的路径。 如果回溯到栈为空时还没有结果,则无解。

  Use the queue to save all the traversable positions and expand the traversable routes in the queue layer by layer until it reaches the exit position. If the last queue is empty, there is no solution.

  使用队列保存所有的可遍历位置,将队列中的可遍历路径逐层展开,直到到达出口位置。 如果最后队列为空,则无解。

Source Code

源代码

#include <iostream>
#include <algorithm>
#include <vector>
#include <stack>
#include <queue>
using namespace std;

struct Box {
    int x;
    int y;
    int nextDirection;
    Box(int x, int y, int nextDirection) : x(x), y(y), nextDirection(nextDirection) {};
};

struct Box_1 {
    int x;
    int y;
    Box_1* prior; // The pointer to the prior point
    Box_1(int x, int y, Box_1* prior) : x(x), y(y), prior(prior) {};
};

vector<vector<int>> GenerateMaze() {
    int row, col;
    cout << "The number of rows and columns:" << endl;
    cin >> row >> col;

    cout << "Input the maze:" << endl;
    // The maze should be expanded by 2 lengths to comprise the frame of the maze
    vector<vector<int>> Maze(row + 2, vector<int>(col + 2, 1));
    for (int i = 1; i < row + 1; i++) {
        for (int j = 1; j < col + 1; j++) {
            cin >> Maze[i][j];
        }
    }
    return Maze;
}

bool IsValidPosition(int x, int y, vector<vector<int>> Maze) { // Verify whether the initial and exit position is valid
    if (x <= 0 || y <= 0 || Maze[x][y] == 1) {
        cout << "Invaild postion!" << endl;
        return false;
    }
    else {
        return true;
    }
}

bool SetThePosition(vector<vector<int>> Maze, int& x, int& y, int& desX, int& desY) {
    cout << "The initial position:" << endl;
    cin >> x >> y;
    if (!(IsValidPosition(x, y, Maze))) {
        return false; 
    } 

    cout << "The exit position:" << endl;
    cin >> desX >> desY;
    if (!(IsValidPosition(desX, desY, Maze))) { 
        return false; 
    }
    return true;
}

bool Solution_DFS(Box& entity, stack<Box>& Path, vector<vector<int>>& Maze) {
    int desX, desY; // The index of the exit point
    if (!SetThePosition(Maze, entity.x, entity.y, desX, desY)) { 
        return false; 
    }

    Path.push(entity);
    Maze[entity.x][entity.y] = -1; // Set the position that has been visited is -1 to avoid endless loop

    while (!Path.empty()) {
        if (Path.top().x == desX && Path.top().y == desY) { // If the entity reaches the destination, return true
            return true; 
        } 
        entity = Path.top(); // Update the current position (where the entity arrived)
        Box temp = entity; // Define a temporary entity for seeking the path
        bool pathFound = false; // Before a new path is found, the flag is false

        while (temp.nextDirection <= 4 && !pathFound) { // Search a new path in four directions
            switch (temp.nextDirection) {
            case 1: temp.x++; break;
            case 2: temp.y++; break;
            case 3: temp.x--; break;
            case 4: temp.y--; break;
            }
            temp.nextDirection++;
            int direction = temp.nextDirection; // Reserve the direction for the next search
            if (Maze[temp.x][temp.y] == 0) { // If a new path is found
                pathFound = true;
                Maze[temp.x][temp.y] = -1; // Occupy the position
                Path.top().nextDirection = temp.nextDirection; // Save the direction 
                temp.nextDirection = 1; // Reset the direction to 1 for the next search
                Path.push(temp); // Add the new path to the whole path
            }
            else {
                temp = entity; // Reset to the intial position
                temp.nextDirection = direction; // Save the last searching direction
            }
        }
        if (!pathFound) { // If the new path cannot be found
            Path.pop(); // pop the top element, keeping searching in the previous position
            Maze[entity.x][entity.y] = 0; // Recover the position that has been occupied
        }
    }
    return false; // If no path towards the destination can be found, return false to declare that the search fails
}

bool Solution_BFS(Box_1& entity, queue<Box_1>& Path, vector<vector<int>>& Maze) {
    int desX, desY;
    if (!SetThePosition(Maze, entity.x, entity.y, desX, desY)) {
        return false;
    }

    Path.push(entity);  
    Maze[entity.x][entity.y] = -1;
    if (entity.x == desX && entity.y == desY) {
        return true;
    }

    while (!Path.empty()) {
        entity = Path.front(); 
        Box_1* front = new Box_1(entity.x, entity.y, entity.prior); // Allocate memory for the front element so that the paths can be connected
        Box_1 temp = entity;
        int direction = 1;

        while (direction <= 4) {
            switch (direction) {
            case 1: temp.x++; break;
            case 2: temp.y++; break;
            case 3: temp.x--; break;
            case 4: temp.y--; break;
            }
            direction++;
            if (Maze[temp.x][temp.y] == 0) {
                Maze[temp.x][temp.y] = -1;
                temp.prior = front; // Connect with the prior path
                Path.push(temp);
                if (temp.x == desX && temp.y == desY) {
                    return true;
                }
            }
            temp = entity;
        }
        Path.pop();
    }
    return false;
}

void DisplayThePath_DFS(vector<vector<int>> Maze) {
    for (int i = 0; i < Maze.size(); i++) {
        for (int j = 0; j < Maze[i].size(); j++) {
            if (Maze[i][j] == 1) {
                cout << "O ";
            }
            else if (Maze[i][j] == 0) {
                cout << "  ";
            }
            else if (Maze[i][j] == -1) {
                cout << "* ";
            }
        }
        cout << endl;
    }

}

void DisplayThePath_BFS(vector<vector<int>> Maze, Box_1* index) {
    while (index != nullptr) {
        Maze[index->x][index->y] = -1;
        index = index->prior;
    }
    for (int i = 0; i < Maze.size(); i++) {
        for (int j = 0; j < Maze[i].size(); j++) {
            if (Maze[i][j] == 1) {
                cout << "O ";
            }
            else if (Maze[i][j] == 0) {
                cout << "  ";
            }
            else if (Maze[i][j] == -1) {
                cout << "* ";
            }
        }
        cout << endl;
    }
    return;
}

void PrintThePath_DFS(stack<Box> Path) {
    Box i(0, 0, 0);
    vector<Box> buf(Path.size(), i);
    for (int j = Path.size() - 1; j >= 0; j--) {
        buf[j] = Path.top();
        Path.pop();
    }
    cout << "The path is:" << endl;
    for (Box i : buf) {
        cout << '(' << i.x << ',' << i.y << ')' << endl;
    }
    return;
}

void PrintThePath_BFS(Box_1 *path) {
    Box_1* index = path;
    stack<Box_1> temp;
    while (index != nullptr) {
        temp.push(*index);
        index = index->prior;
    }
    while (!temp.empty()) {
        cout << '(' << temp.top().x << ',' << temp.top().y << ')' << endl;
        temp.pop();
    }
}

int main()
{
    Box entity(0, 0, 1);
    stack<Box> Path;
    vector<vector<int>> Maze = GenerateMaze();
    if (Solution_DFS(entity, Path, Maze)) {
        PrintThePath_DFS(Path);
        DisplayThePath_DFS(Maze);
    }
    else {
        cout << "No path found!" << endl;
    }
    
    Box_1 entity_1(0, 0, nullptr);
    vector<vector<int>> Maze_1 = GenerateMaze();
    vector<vector<int>> temp = Maze_1;
    queue<Box_1> Path_1;
    if (Solution_BFS(entity_1, Path_1, Maze_1)) {
        DisplayThePath_BFS(temp, &Path_1.back());
        PrintThePath_BFS(&Path_1.back());
    }
    else {
        cout << "No path found!" << endl;
    }
    return 0;
}

Analysis

分析

  • DFS (Depth First Search):
  • 深度优先搜索

Traits: All the paths are found, and it uses backtracking, saves the location in each search and goes deeper, and when it's done, it goes back to the next location, until it has searched all the deepest locations (These traits can be manifested typically in Trees traversal). 

特点:使用回溯。在每次搜索中保存位置并继续深入,完成后,回溯下一个位置,直到搜索完所有最深的位置(这些特征可以很典型体现在树遍历)。

Pros: There is no need to record precursor nodes like breadth-first search, which will occupy extra space especially when the problem’s scale is very large.

优点:不需要像广度优先搜索那样记录前驱节点,这会占用额外的空间,尤其是当问题规模很大时。

Cons: Cannot find the shortest path directly.

缺点:不能直接找到最短路径。

  • BFS (Breadth First Search):
  • 广度优先搜索

Traits: Starting from a chosen point, then take one step at a time from each direction of the point to the end (i.e. continue in the next direction after taking one step in one direction). If it can't find the solution, it returns the predetermined value. If it can find the solution, it returns the desired solution. 

特点:从选定的点开始,然后从该点的每个方向一步一步到终点(即在一个方向上迈出一步后继续下一个方向)。 如果找不到解,则返回预定值。 如果它可以找到解决方案,它会返回所需的解决方案。

Pros: The path found is the shortest path.

优点:找到的路径即是最短路径。

Cons: Records of the precursor nodes is needed.

缺点:需要记录前驱节点。

Personal Experience

  After implementing this algorithm, not only have I learned some new knowledge, but deepened and upgraded the knowledge I have already known. The core of this algorithm is the two ways of searching (BFS and DFS) and two data structures (Stack and Queue) that are used to implement them. To implement this algorithm, I used two ways of traversal (also BFS and DFS) in Binary Trees as the analogy. The idea of solving this problem is relatively simple, but there were numerous problems in the implementation (such as how to record the precursor nodes in BFS solution, how to implement the searching process, etc.), which took me a lot of work and time. Nevertheless, the whole process paid off, especially when I was trying to comprehend the ingenuity and beauty of this algorithm.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值