ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Leetcode迷宫问题 BFS 整体思路及实现

2021-11-01 21:34:41  阅读:856  来源: 互联网

标签:pq dist cur int 迷宫 num BFS vector Leetcode


迷宫问题

来自小白上了算法课之后觉得自己的coding能力是在太差了,记录一下大佬的leetcode499迷宫问题的解答,加上了自己的注释和理解。还有结构图。

leetcode三道题都是BFS的延伸问题进行一些改动,
下面是一些区别的点:
1.490结果给出是否能到达即可,目的地是星(即停到此处才可以)
2.505结果给出能到达的最短路径即可,结果输出可以或者不可以,目的地是星(即停到此处才可以)
3.499结果是给出最短路径并进行字符串比较(输出包括上下左右的方向的字符串),目的地是洞(即路径中遇到过该点即可)(这个和算法大作业的第二个迷宫问题完全一样)
4.我们算法大作业的第一个迷宫问题,结果是给出最短路径并进行字符串比较(输出包括上下左右的方向的字符串),目的地是星(即停到此处才可以)
5.我们算法大作业的第三个迷宫问题,即生成一个迷宫,利用Prim算法,最后生成一下目标点即可。
6.这几道迷宫问题区别于一般的BFS的区别是,没有撞到墙就一直走,而不是每走一步就可以停下
7.关于BFS的模板有大佬的讲解视频
BFS模板讲解
8.这个是我迷宫问题的解题来源,也感谢大佬
迷宫问题解题来源
9.这个是我生成迷宫的解题来源,这个是Prim算法,感谢大佬
生成迷宫解题来源

算法思路

leetcode流程图

针对输出路径的流程图,不是很精确,大意是这样
针对输出路径的流程图,不是很精确,大意是这样

生成迷宫

基本算法流程:利用Prim算法,算法思路为
1.让迷宫全是墙.
2.选一个单元格作为迷宫的通路(我一般选择起点),然后把它的邻墙(上下左右)放入列表
3.当列表里还有墙时
①.从列表里随机选一个墙,如果这面墙分隔的两个单元格只有一个单元格被访问过:
(a)那就从列表里移除这面墙,即把墙打通,让未访问的单元格成为迷宫的通路
(b)把这个格子的墙加入列表
②.如果墙两面的单元格都已经被访问过(都打通了),那就从列表里移除这面墙

生成迷宫的流程图
生成迷宫流程图

代码实现

leetcode490

这个是leetcode490 BFS解法的代码

#include <iostream>
#include<vector>
#include <queue>
using namespace std;
class Solution{
public:
    bool hasPath(vector<vector<int>>&maze,vector<int>&start,vector<int>&destination){
        int m=maze.size();
        int n=maze[0].size();
        vector<int>di={-1,0,1,0};
        vector<int>dj={0,-1,0,1};
        queue<vector<int>>q;
        q.push(start);

        vector<vector<int>>visited(m,vector<int>(n,0));
        visited[start[0]][start[1]]=1;

        while(!q.empty()){
            vector<int>cur=q.front();
            q.pop();

            if(cur==destination){
                return true;
            }
            for(int d=0;d<4;d++){
                int i=cur[0];
                int j=cur[1];

                while(i>=0&&i<m&&j>=0&&j<n&&maze[i][j]==0){
                    i+=di[d];
                    j+=dj[d];
                }
                i-=di[d];
                j-=dj[d];
                if(!visited[i][j]){
                    q.push({i,j});
                    visited[i][j]=1;
                }
            }
        }
        return false;
    }
};
int main() {
    vector<vector<int>>maze={{0,0,1,0,0},{0,0,0,0,0},{0,0,0,1,0},{1,1,0,1,1},{0,0,0,0,0}};
    vector<int>start={0,4};
    vector<int>des={4,4};
    //ans=true;
    Solution s;
    cout<<s.hasPath(maze,start,des);
    return 0;
}

leetcode505

#include <iostream>
#include<vector>
#include <queue>
using namespace std;
class Solution{
public:
    int shortestDistance(vector<vector<int>>&maze,vector<int>&start,vector<int>&destination){
        int m=maze.size();
        int n=maze[0].size();
        vector<int>di={-1,0,1,0};
        vector<int>dj={0,-1,0,1};

        priority_queue<vector<int>>pq;//{-dist,i,j}
        pq.push({0,start[0],start[1]});

        vector<vector<int>>visited(m,vector<int>(n,0));
       // visited[start[0]][start[1]]=1;

        while(!pq.empty()){
            vector<int>cur=pq.top();
            pq.pop();
            if(visited[cur[1]][cur[2]]){
                continue;
            }

            visited[cur[1]][cur[2]]=1;

            if(vector<int>{cur[1],cur[2]}==destination){
                return -cur[0];
            }
            for(int d=0;d<4;d++){
                int i=cur[1];
                int j=cur[2];

                int dist=cur[0];//当前距离记录为负值

                while(i>=0&&i<m&&j>=0&&j<n&&maze[i][j]==0){
                    i+=di[d];
                    j+=dj[d];
                    dist--;
                }
                i-=di[d];
                j-=dj[d];
                dist++;
                if(!visited[i][j]){
                    pq.push({dist,i,j});
                  //  visited[i][j]=1;
                }
            }
        }
        return -1;
    }
};
int main() {
    vector<vector<int>>maze={{0,0,1,0,0},{0,0,0,0,0},{0,0,0,1,0},{1,1,0,1,1},{0,0,0,0,0}};
    vector<int>start={0,4};
    vector<int>des={4,4};
    //ans=true;
    Solution s;
    //ans=12
    cout<<s.shortestDistance(maze,start,des);
    return 0;
}

leetcode505输出路径解法,主函数包括两个测试用例

#include <iostream>
#include<vector>
#include<string>
#include<functional>
#include<queue>
using namespace std;
struct position{//定义结构体记录距离方向和当前路径的字符串
    int dist;
    int i;
    int j;
    string str;
};
class Solution{
public:
    string findShortestWay(vector<vector<int>>& maze,vector<int>& ball,vector<int>&destination){
        int m=maze.size();
        int n=maze[0].size();//迷宫规模

        vector<int> di{-1,0,1,0};//定义四个方向
        vector<int> dj{0,-1,0,1};

        vector<char>chars{'u','l','d','r'};

        string result="-1";
        int min_dist=INT_MAX;//初始化最小值

        priority_queue<position,vector<position>,function<bool(position& p1,position&
        p2)>> pq([](position& p1,position& p2) -> bool { return p1.dist > p2.dist;});
        //把自定义的类放在优先队列里,按dist从小到大排列

        position pos=position{0,ball[0],ball[1],""};//起始点
        pq.push(pos);

        vector<vector<int>>visited(m,vector<int>(n,0));
        //查重的二维数组

        while(!pq.empty()){
            position cur=pq.top();//取出当前点
            pq.pop();

            if(cur.dist>min_dist){//如果大于最小值,跳过
                continue;
            }

            if(vector<int>{cur.i,cur.j}==destination){//如果当前点目的地
                if(cur.dist<min_dist){//如果当前距离小于最小距离,更新最小值和走过的路径
                    result=cur.str;
                    min_dist=cur.dist;
                }else if(cur.dist==min_dist&&//如果当前距离等于最小距离,则比较字符串,更新走过的路径
                         lexicographical_compare(cur.str.begin(),cur.str.end(),
                                                 result.begin(),result.end())){
                    result=cur.str;
                }
            }
            visited[cur.i][cur.j]=1;

            for(int d=0;d<4;d++){//四个方向
                int i=cur.i;
                int j=cur.j;

                int dist=cur.dist;//记录距离最小值
                string str=cur.str;//记录当前方向

                str+=chars[d];//加上当前方向

                while(i>=0&&i<m&&j>=0&&maze[i][j]==0){
                    i+=di[d];//没有撞墙一直走
                    j+=dj[d];
                    dist++;
                }
                i-=di[d];//撞上了就退一步
                j-=dj[d];
                dist--;

                if(!visited[i][j]){
                    position pos=position{dist,i,j,str};//记录当前位置
                    pq.push(pos);
                }
            }
        }
        return result;

    }
};
int main() {
    vector<vector<int>>maze={{0,0,1,0,0},{0,0,0,0,0},{0,0,0,1,0},{1,1,0,1,1},{0,0,0,0,0}};
    vector<int>ball1={0,4};
    vector<int>hole1={4,4};
    Solution s;
    //ldldrdr
    vector<int>ball2={0,4};
    vector<int>hole2={3,2};
    //cout<<s.findShortestWay(maze,ball1,hole1);
    cout<<s.findShortestWay(maze,ball2,hole2);
    return 0;
}

leetcode499

这个是leetcode499 BFS解法的代码

#include <iostream>
#include<vector>
#include<string>
#include<functional>
#include<queue>
using namespace std;
struct position{//定义结构体记录距离方向和当前路径的字符串
    int dist;
    int i;
    int j;
    string str;
};
class Solution{
public:
    string findShortestWay(vector<vector<int>>& maze,vector<int>& ball,vector<int>&hole){
        int m=maze.size();
        int n=maze[0].size();//迷宫规模

        vector<int> di{-1,0,1,0};//定义四个方向
        vector<int> dj{0,-1,0,1};

        vector<char>chars{'u','l','d','r'};

        string result="impossible";
        int min_dist=INT_MAX;//初始化最小值

        priority_queue<position,vector<position>,function<bool(position& p1,position&
        p2)>> pq([](position& p1,position& p2) -> bool { return p1.dist > p2.dist;});
        //把自定义的类放在优先队列里,按dist从小到大排列

        position pos=position{0,ball[0],ball[1],""};//起始点
        pq.push(pos);

        vector<vector<int>>visited(m,vector<int>(n,0));
        //查重的二维数组

        while(!pq.empty()){
            position cur=pq.top();//取出当前点
            pq.pop();

            if(cur.dist>min_dist){//如果大于最小值,跳过
                continue;
            }

            if(vector<int>{cur.i,cur.j}==hole){//如果当前点是洞
                if(cur.dist<min_dist){//如果当前距离小于最小距离,更新最小值和走过的路径
                    result=cur.str;
                    min_dist=cur.dist;
                }else if(cur.dist==min_dist&&//如果当前距离等于最小距离,则比较字符串,更新走过的路径
                lexicographical_compare(cur.str.begin(),cur.str.end(),
                                        result.begin(),result.end())){
                    result=cur.str;
                }
            }
            visited[cur.i][cur.j]=1;

            for(int d=0;d<4;d++){//四个方向
                int i=cur.i;
                int j=cur.j;

                int dist=cur.dist;//记录距离最小值
                string str=cur.str;//记录当前方向

                str+=chars[d];//加上当前方向

                while(i>=0&&i<m&&j>=0&&maze[i][j]==0){
                    if(vector<int>{i,j}==hole){//如果掉到洞里
                        i+=di[d];
                        j+=dj[d];
                        dist++;
                        break;
                    }
                    i+=di[d];//没有撞墙一直走
                    j+=dj[d];
                    dist++;
                }
                i-=di[d];//撞上了就退一步
                j-=dj[d];
                dist--;

                if(!visited[i][j]){
                    position pos=position{dist,i,j,str};//记录当前位置
                    pq.push(pos);
                }
            }
        }
        return result;

    }
};
int main() {
    vector<vector<int>>maze={{0,0,0,0,0},{1,1,0,0,1},{0,0,0,0,0},{0,1,0,0,1},{0,1,0,0,0}};
    vector<int>ball={4,3};
    vector<int>hole={0,1};
    Solution s;
    cout<<s.findShortestWay(maze,ball,hole);
    return 0;
}

生成迷宫代码

#include <bits/stdc++.h>
using namespace std;
#define m 11//row
#define n 11
#define down 1
#define right 2
#define left 4
#define up 8
#define WALL -1
#define NOTHING 2

struct block{
    int row,column,direction;
    block(int _row,int _column,int _direction){
        row = _row;
        column = _column;
        direction = _direction;
    }
};
struct point {
    int x;
    int y;
}start,endd;

vector<block> myblock;
int x_num=1,y_num=1;//矿工位置
int G[100][100];

void init() {
    //将地图全部置为墙
    memset(G,WALL,sizeof(G));
    //定义起始点
    G[1][1] = NOTHING;
    start.x = start.y = 1;
    endd.x=endd.y=1;
}
void FindBlock() {
    //找出与当前位置相邻的墙
    if(x_num+1<=m && G[x_num+1][y_num] == WALL) {//down
        myblock.push_back(block(x_num+1,y_num,down));
    }
    if(y_num+1<=n && G[x_num][y_num+1] == WALL) {//right
        myblock.push_back(block(x_num,y_num+1,right));
    }
    if(x_num-1>=1 && G[x_num-1][y_num] == WALL) {//up
        myblock.push_back(block(x_num-1,y_num,up));
    }
    if(y_num-1>=1 && G[x_num][y_num-1] == WALL) {//left
        myblock.push_back(block(x_num,y_num-1,left));
    }
}

int main() {
    init();
    srand((unsigned)time(NULL));//随机数种子
    FindBlock();
    //第一步压入两堵墙(起点右边和起点下面)进入循环
    while(myblock.size()) {
        int BlockSize = myblock.size();
        //随机选择一堵墙(生成0 ~ BlockSize-1之间的随机数,同时也是vector里墙的下标)
        int randnum = rand() % BlockSize;
        block SelectBlock = myblock[randnum];
        x_num = SelectBlock.row;//矿工来到我们“选择的墙”这里
        y_num = SelectBlock.column;
        //根据当前选择的墙的方向进行后续操作
        //此时,起始点 选择的墙 目标块 三块区域在同一直线上
        //我们让矿工从“选择的墙”继续前进到“目标块”
        //矿工有穿墙能力 :)
        printf("randnum:%d,myblockSize:%d,xnum:%d,ynum:%d\n",randnum,BlockSize,x_num,y_num);
        switch(SelectBlock.direction) {
            case down: {x_num++;break;
            }
            case right: {y_num++;break;
            }
            case left: {y_num--;break;
            }
            case up: {x_num--;break;
            }
        }
        //目标块如果是墙
        if(G[x_num][y_num]==WALL) {
            //打通墙和目标块
            G[SelectBlock.row][SelectBlock.column] = G[x_num][y_num] = NOTHING;
            //再次找出与矿工当前位置相邻的墙
            FindBlock();
        }
        else{//如果不是呢?说明我们的矿工挖到了一个空旷的通路上面 休息一下就好了
            //relax
        }
        //删除这堵墙(把用不了的墙删了,对于那些已经施工过了不必再施工了,同时也是确保我们能跳出循环)
        myblock.erase(myblock.begin()+randnum);
    }
    //随机生成结束点
    while(endd.x==1){
        int randx = rand() % n;
        int randy = rand() % m;
        if(G[randx][randy]==NOTHING)//加上这行表示结束点一定是有解的,不加表示不一定有解
        {endd.x= randx;//矿工来到我们“选择的墙”这里
        endd.y = randy;}
    }
    //输出迷宫
    for (int i=0;i<=m+1;i++) {
        for (int j=0;j<=n+1;j++) {
            if(G[i][j] == NOTHING) {
                printf("0 ");
            }
            else {
                printf("1 ");
            }
        }
        printf("\n");
    }
    printf("start:(%d,%d)\n",start.x,start.y);
    printf("end:(%d,%d)\n",endd.x,endd.y);
    char c;
    cin>>c;

    return 0;
}

标签:pq,dist,cur,int,迷宫,num,BFS,vector,Leetcode
来源: https://blog.csdn.net/weixin_45719088/article/details/121006779

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有