路径搜索问题

本文介绍了如何解决路径搜索问题,包括基于二维数组的迷宫问题。分析了广度优先搜索(BFS)、深度优先搜索(DFS)策略,并讨论了在大规模迷宫中,A*算法作为更智能的选择。文章详细解释了A*算法的原理,包括实际代价、预估代价和曼哈顿距离。最后,通过测试数据比较了不同算法在不同规模迷宫中的效率。
摘要由CSDN通过智能技术生成

对简单路径问题的分析

问题:假设有这么一个二维数组,1代表墙不可以通过,0代表通路可以通过,试问给出起点(1, 1)和终点(7,7)可不可以给出一条通路、最短通路、以及所有通路(假设每个点都可以找八个方向)。(这些问题有人可能叫迷宫问题等,但是我理解的迷宫你只能知道起点的坐标,不能知道终点,所以对于知道起点和终点的问题我更愿意叫做路径搜索问题)

1 1 1 1 1 1 1 1 1
1 0 0 1 1 0 1 1 1
1 1 0 0 0 0 0 0 1
1 0 1 0 0 1 1 1 1
1 0 1 1 1 0 0 1 1
1 1 0 0 1 0 0 0 1
1 0 1 1 0 0 0 1 1
1 1 1 1 1 1 1 0 1
1 1 1 1 1 1 1 1 1

分析:对于这个问题,我们可以联想到有最基本的广度优先策略和深度优先策略。

1.深度优先就是一条道走到底,走不去了就"回溯"(回溯的实现就是利用递归的栈来实现,栈顶函数弹出栈,就可以达到回溯的目的)知道找到终点为止。
这个算法就是递归,用到了系统的函数栈。
2.广度优先优先,就是从起点开始,假设你可以分身你可以从该点开始同时对这八个方向进行探索,然后对每个可以走的点继续利用"分身"来对每个点
来进行上面所述的对八个方向同时进行探索知道找到终点为止。这个算法主要用到了队列,这时最先找到的就是最短的路径。

对上述问题的解决

我们已经知道最短路径的探索方法,那么一条普通的路径就可以解决。那么对于探索上述迷宫的所有路径,我们只需要利用一下深度优先的"回溯"原理就好。我们对于找到一条通路时,不要直接输出路径后就return程序,而应该和遇到障碍时一样,来进行回溯。直到回溯完起点的八个为止为止。这个有点像求八皇后问题的回溯法一样。

代码实现

广度优先求最短路径

public class Point {             //首先定义了一个位置类, (x, y)代表坐标
    public final int x;
    public final int y;
    public Point(int x, int y){  //构造器,用于初始化位置 
        this.x = x;
        this.y = y;
    }
}

import java.util.Date;
import java.util.LinkedList;
import java.util.Scanner;

public class BfsShort {
    private final int[][] maze;          //定于一个二维数组来表示迷宫
    private final Point s;        //定义了起始点
    private final Point e;       //定义了终点
    private Point[][] patTo;   //用于保存路径                                     //定义了八个方向
    private final int[][] directory = {
  {0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}, {-1, -1}, {-1, 0}, {-1, 1}};
    private LinkedList<Point> stack = new LinkedList<>();  //没有用双向链表来模拟队列(队列的名字写成了栈的英语单词,忘记改了,手动狗头)
    private BfsShort(int[][] maze, Point s, Point e){                       //构造器读入地图,起点和                      终点
        this.maze = maze;
        this.s = s;
        this.e = e;
        patTo = new Point[maze.length][maze[0].length];
        bfs();                             //调用了广度优先函数
        print();                           //打印出最段路径
    }
    private void bfs(){
        stack.addLast(this.s);                                                       //首先将起点加入队列
        this.maze[s.x][s.y] = 2;                                                    //将走过的点标记为2,表示走 过了
        while (!stack.isEmpty()){                                      //当队列为空时程序结束,此时没有找到路径,直接将用于patTo保存路径的数组赋值为null,打印出一行话"该路径没有路径"
            Point p = stack.removeFirst();
            int x = p.x;
            int y = p.y;
            for (int i = 0; i < 8; i++) {            //开始分身,对八个方将进行探索
                int t = x + directory[i][0];        //下一个位置
                int v = y + directory[i][1];
                if (judge(t , v)){                   //判断下一个位置是不是可以走,如果可以走judge函数返回ture;
                    patTo[t][v] = p;             //将当前位置指向父位置,这样便于最后打印出路径
                     this.maze[t][v] = 2;    //将走过的路标记为2
                    stack.addLast(new Point(t, v));   //将该位置入队列
                    if (t == this.e.x && v == this.e.y)          //当找到终点时,直接找到路径程序结束,如果没有找到终点继续while循环
                        return;
                }
            }
        }
        patTo = null;
        System.out.println("该迷宫没有路径");
    }
    private void print(){
        if (patTo == null)             //如果没有找到路径,直接结束程序,不打印
            return;
        Point temp = this.e;         //如果有路径,则以终点为起始点开始,来继续对路径的打印
        while (temp.x != this.s.x || temp.y != this.s.y){   //直到找到起点循环结束
            System.out.print("[" + temp.x + " " + temp.y + "]");
            temp = patTo[temp.x][temp.y];              //打印了temp位置后,然后找到它的父位置
        }
        System.out.println("[" + this.s.x + " " + this.s.y + "]");   //最后打印出起点位置
    }
    private boolean judge(int x, int y){
        if (x < 0 || x >= maze.length || y < 0 || y >= maze[0].length)   //如果越界,直接返回false
            return false;
        if (maze[x][y] == 1 || maze[x][y] == 2)    //如果是障碍或者已经走过了直接返回false
            return false;
        else return true;    //如果上述条件都不满足,那么就可以走,返回ture
    }
    public BfsShort(int[][] maze, int x, int y, int g, int h){           //对于用户只提供该方法其他方法全部为私有方法。
        this(maze, new Point(x, y), new Point(g, h));
    }
}

上述代码只对用于提供接口 public BfsShort(int[][] maze, int x, int y, int g, int h),打印出该迷宫的最短路径
测试上述迷宫:

   public static void main(String[] args) {
        int[][] maze = new int[9][9];
        Scanner in = new Scanner(System.in);
        for (int i = 0; i < maze.length; i++)
            for (int j = 0; j < maze[0].length; j++)
                maze[i][j] = in.nextInt();
        BfsShort f = new BfsShort(maze, 1, 1, 7, 7);
    }

结果如下
在这里插入图片描述

深度优先打印出所有路径

这个代码也会利用到上述的Point类,和上述代码相同含义的遍历已经判断我就不在解释第二遍了
具体的代码实现

import java.util.LinkedList;
import java.util.Scanner;

public class DfsAll {
    //深度优先打印出迷宫所有可行的路径
    private int count = 0;
    private int[][] maze;
    private final Point s;
    private final Point e;
    private LinkedList<Point> patTo = new LinkedList<>();  //利用链表,来储存当前探索的路径
    private final int[][] directory = {
  {0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}, {-1, -1}, {-1, 0}, {-1, 1}};
    private DfsAll(int[][] maze, Point s, Point e){
        this.maze = maze;
        this.s = s;
        this.e = e;
        dfs(this.s);           //调用深度优先打印打印出所有的路径
    }
    private boolean judge(int x, int y){         
        if (x < 0 || x >= maze.length || y < 0 || y >= maze[0].length)
            return false;
        if (maze[x][y] == 1 || maze[x][y] == 2)
            return false;
        else return true;
    }
    private void dfs(Point s){
        this.patTo.addLast(s);              //将s加入路径链表中
        if (maze[this.e.x][this.e.y] == 2)   //如果该点s为终点
            printPat();                               //打印出一条路径
        else {                                          //如果不是的话,找到可以深入的点深入递归下去
            int x = s.x;
            int y = s.y;
            for (int i = 0; i < 8; i++)
                if (judge(x + directory[i][0], y + directory[i][1])){    //如果该点可以走的话
                    this.maze[x + directory[i][0]][y + directory[i][1]] = 2;   //标记该点已经走过
                    dfs(new Point(x + directory[i][0], y + directory[i][1]));   //以该点为s继续递归
                }
        }
        this.patTo.removeLast();      
        this.maze[s.x][s.y] = 0;
       //以上两行是重点: 当走不去了,或者该点已经是终点了,将该点从路径数组中除出,回到上一步的for循环中,对其他剩下的方向进行探索,不过没有路径继续向上回溯,如果有继续向下走,该程序直达把其他的八个方向依次探索完为止,这里有点八皇后问题的味道,可以对比着看。 
         }
    private void printPat(){   //打印出一条路径
        if (this.patTo.isEmpty())
            return;
        System.out.print(++count + ":");    //count用于记录路径的数量
        for (Point to : this.patTo)
            System.out.print("[" + to.x + " " + to.y + "]");
        System.out.println();
    }
    public DfsAll(int[][] maze, int x, int y, int g, int h){
        this(maze, new Point(x, y), new Point(g, h));
    }
}

测试如下:

public static void main(String[] args) {
        int[][] maze = new int[9][9];
        Scanner in = new Scanner(System.in);
        for (int i = 0; i < maze.length; i++)
            for (int j = 0; j < maze[0].length; j++)
                maze[i]
  • 11
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值