当前位置: 首页 > 面试题库 >

递归蛮力迷宫求解器Java

劳昊明
2023-03-14
问题内容

为了编写一个解决C程序的蛮力迷宫,我首先编写了这个Java程序来测试一个想法。我对C还是很陌生,打算在Java中正确处理后将其转换。结果,我试图远离arraylist,fancy库等,以便更轻松地转换为C。该程序需要生成最短步骤的单个宽度路径来解决迷宫问题。我认为我的问题可能在于将通过每个递归传递的路径存储数组碎片化。感谢您的关注。-乔

maze:

1 3 3 3 3 
3 3 3 3 3 
3 0 0 0 3 
3 0 3 3 3 
0 3 3 3 2


Same maze solved by this program:
4 4 4 4 4 
4 4 4 4 4 
4 0 0 0 4 
3 0 3 3 4 
0 3 3 3 2

代码中解释了数字符号

    public class javamaze {

static storage[] best_path;
static int best_count;
static storage[] path;

//the maze - 1 = start; 2 = finish; 3 = open path
static int maze[][] = {{1, 3, 3, 3, 3}, 
    {3, 3, 3, 3, 3},
    {0, 0, 0, 0, 3},
    {0, 0, 3, 3, 3},
    {3, 3, 3, 3, 2}};

public static void main(String[] args) {

    int count1;
    int count2;

    //declares variables used in the solve method
    best_count = 0;
    storage[] path = new storage[10000];
    best_path = new storage[10000];
    int path_count = 0;


    System.out.println("Here is the maze:");
    for(count1 = 0; count1 < 5; count1++) {
        for(count2 = 0; count2 < 5; count2++) {
            System.out.print(maze[count1][count2] + " ");   
        }                       
        System.out.println("");         
    }

    //solves the maze
    solve(findStart()/5, findStart()%5, path, path_count);

    //assigns an int 4 path to the maze to visually represent the shortest path
    for(int count = 0; count <= best_path.length - 1; count++)
        if (best_path[count] != null)
            maze[best_path[count].getx()][best_path[count].gety()] = 4;

    System.out.print("Here is the solved maze\n");

    //prints the solved maze
    for(count1 = 0; count1 < 5; count1++) {
        for(count2 = 0; count2 < 5; count2++){
            System.out.print(maze[count1][count2] + " ");
        }
        System.out.print("\n");
    }
}

//finds maze start marked by int 1 - this works perfectly and isn't related to the problem
public static int findStart() {
    int count1, count2;
    for(count1 = 0; count1 < 5; count1++) {
        for(count2 = 0; count2 < 5; count2++) {
            if (maze[count1][count2] == 1)
                return (count1 * 5 + count2);
        }
    }
    return -1;
}

//saves path coordinate values into a new array
public static void save_storage(storage[] old_storage) {
    int count;
    for(count = 0; count < old_storage.length; count++) {
        best_path[count] = old_storage[count];
    }
}

//solves the maze
public static Boolean solve(int x, int y, storage[] path, int path_count) {

    //checks to see if grid squares are valid (3 = open path; 0 = wall
    if (x < 0 || x > 4) { //array grid is a 5 by 5
        //System.out.println("found row end returning false");
        return false;
    }
    if (y < 0 || y > 4) {
        //System.out.println("Found col end returning false");
        return false;
    }

    //when finding finish - records the number of moves in static int best_count
    if (maze[x][y] == 2) {
        if (best_count == 0 || best_count > path_count) {
            System.out.println("Found end with this many moves: " + path_count);
            best_count = path_count;
            save_storage(path); //copies path counting array into a new static array
        }
    }
    //returns false if it hits a wall
    if (maze[x][y] == 0)
        return false;

    //checks with previously crossed paths to prevent an unnecessary repeat in steps
    for(storage i: path) 
        if (i != null)
            if (i.getx() == x && i.gety() == y) 
                return false;

    //saves current recursive x, y (row, col) coordinates into a storage object which is then added to an array.
    //this array is supposed to fragment per each recursion which doesn't seem to - this may be the issue
    storage storespoints = new storage(x, y);
    path[path_count] = storespoints;

    //recurses up, down, right, left
    if (solve((x-1), y, path, path_count++) == true || solve((x+1), y, path, path_count++) == true ||
            solve(x, (y+1), path, path_count++) == true || solve(x, (y-1), path, path_count++) == true) {
        return true;
    }

    return false;
}
}

//stores (x, y) aka row, col coordinate points
class storage {

private int x;
private int y;

public storage(int x, int y) {
    this.x = x;
    this.y = y;
}
public int getx() {
    return x;
}
public int gety() {
    return y;
}
public String toString() {
    return ("storage coordinate: " + x + ", " + y + "-------");
}

}

问题答案:

这本来不是要作为答案的,但实际上已经演变成了一个答案。老实说,我认为从Java开始并转向C是一个坏主意,因为这两种语言确实没有什么相似之处,而且您不会帮上忙,因为如果您依赖于Java的任何功能,在移植它时都会遇到严重的问题。有C没有(即大多数)

也就是说,我将草拟一些算法C的东西。

支持结构

typedef
struct Node
{
    int x, y;
    // x and y are array indices
}
Node;

typedef
struct Path
{
    int maxlen, head;
    Node * path;
    // maxlen is size of path, head is the index of the current node
    // path is the pointer to the node array
}
Path;

int    node_compare(Node * n1, Node * n2); // returns true if nodes are equal, else false

void   path_setup(Path * p, Node * n); // allocates Path.path and sets first node
void   path_embiggen(Path * p);        // use realloc to make path bigger in case it fills up
int    path_toosmall(Path * p);        // returns true if the path needs to be reallocated to add more nodes
Node * path_head(Path * p);            // returns the head node of the path
void   path_push(Path * p, Node * n);  // pushes a new head node onto the path
void   path_pop(Path * p);             // pops a node from path

您可能将迷宫格式更改为邻接列表之类的东西。您可以将每个节点存储为掩码,详细说明可以从该节点访问的节点。

迷宫格式

const int // these constants indicate which directions of travel are possible from a node
N = (1 << 0),       // travel NORTH from node is possible
S = (1 << 1),       // travel SOUTH from node is possible
E = (1 << 2),       // travel EAST  from node is possible
W = (1 << 3),       // travel WEST  from node is possible
NUM_DIRECTIONS = 4; // number of directions (might not be 4.  no reason it has to be)

const int
START  = (1 << 4),  // starting  node
FINISH = (1 << 5);  // finishing node

const int
MAZE_X = 4,         // maze dimensions
MAZE_Y = 4;

int maze[MAZE_X][MAZE_Y] = 
{
    {E,        S|E|W,    S|E|W,    S|W       },
    {S|FINISH, N|S,      N|START,  N|S       },
    {N|S,      N|E,      S|E|W,    N|S|W     },
    {N|E,      E|W,      N|W,      N         }
};

Node start  = {1, 2}; // position of start node
Node finish = {1, 0}; // position of end node

我的迷宫与您的迷宫不同:两种格式彼此之间并非完全一对一地映射。例如,您的格式允许更精细的移动,但是我的格式允许单向路径。

请注意,您的格式明确指定了墙的位置。按照我的格式,墙在概念上位于无法通行的任何地方。我创建的迷宫有3个水平墙和5个垂直墙(并且也是封闭的,即有连续的墙围绕着整个迷宫)

为了进行遍历,我将使用深度优先搜索。您可以通过多种方式将标志映射到方向,例如以下所示。由于无论如何都要遍历每个对象,因此访问时间是无关紧要的,因此仅使用数组而不是某种更快的关联容器就足够了。

数据格式到偏移映射

// map directions to array offsets
// format is [flag], [x offset], [y offset]
int mappings[][] =
{
    {N, -1,  0},
    {S,  1,  0},
    {E,  0,  1},
    {W,  0, -1}
}

最后,您的搜索。您可以迭代或递归实现。我的示例使用递归。

搜索算法伪代码

int search_for_path(int ** maze, char ** visited, Path * path)
{
    Node * head = path_head(path);
    Node temp;
    int i;

    if (node_compare(head, &finish)) return 1; // found finish
    if (visited[head->x][head->y])   return 0; // don't traverse again, that's pointless

    visited[head->x][head->y] = 1;
    if (path_toosmall(path)) path_embiggen(path);

    for (i = 0; i < NUM_DIRECTIONS; ++i)
    {
        if (maze[head->x][head->y] & mappings[i][0]) // path in this direction
        {
            temp = {head->x + mappings[i][1], head->y + mappings[i][2]};
            path_push(path, &temp);
            if (search_for_path(maze, visited, path)) return 1; // something found end
            path_pop(path);
        }
    }
    return 0; // unable to find path from any unvisited neighbor
}

要调用此功能,您应该像这样设置所有内容:

调用解算器

// we already have the maze
// int maze[MAZE_X][MAZE_Y] = {...};

// make a visited list, set to all 0 (unvisited)
int visited[MAZE_X][MAZE_Y] = 
{
    {0,0,0,0},
    {0,0,0,0},
    {0,0,0,0},
    {0,0,0,0}
};

// setup the path
Path p;
path_setup(&p, &start);

if (search_for_path(maze, visited, &path))
{
    // succeeded, path contains the list of nodes containing coordinates from start to end
}
else
{
    // maze was impossible
}

值得注意的是,因为我在编辑框中都写了这些,所以我还没有测试过。第一次尝试可能无法正常工作,可能需要花些时间。例如,除非在全局范围内声明了开始和结束,否则会有一些问题。最好将目标节点传递给搜索功能,而不要使用全局变量。



 类似资料:
  • 我正在尝试寻找到EndPotion的路径。这是一个递归函数。请帮助,我要自杀了。 这是给定的地图 我想递归地使用GetPath来到达上面地图中的EndPotion。参数是当前位置、结束位置和地图。对于这个例子,起始位置是(0,0)和结束,EndPotionis是(0,3),右上角。0代表墙壁,1代表路径。 我需要返回一个包含有效点的arraylist到结束位置。虽然我的数组大小始终为0,并且基本大

  • 问题内容: 我正在尝试使用递归编写一个迷宫求解器,似乎它尝试每个方向一次,然后停止,我不知道为什么。如果您发现问题,请告诉我。钥匙0是一个开放空间1是墙壁2是路径的一部分3是迷宫的末端 问题答案: 在过去五个小时中,您已经问过有关此迷宫递归难题的四个问题,这证明它有多复杂。这整个概念的1/0迷宫电网已吸引了我,我想出了一个类,使它成为一个 整体 变得简单许多。如果需要进行递归,那么它将对您没有用,

  • 我得到了一些构建迷宫的代码,以及任何其他需要的东西,抽象迷宫类包含一个抽象方法“makeMove(int row,int col)”。这是我试图编写的解决迷宫的方法,向左、向右、向上、向下移动。 我刚刚开始做这件事,下面是我到目前为止的全部资料。 好的,我让代码运行到不再出现错误的地方。 感谢所有的帮助。

  • 给定一个填充了0和1的二维字符数组,其中0代表一堵墙,1代表一条有效路径,我开发了一个名为findPath(int r,int c)的递归方法,用于在标有“x”的迷宫中找到出口。该方法接收迷宫的当前行和列,并通过N、E、S、W方向,直到找到有效路径,并用“”标记该有效路径。假设所有方向都被一堵墙挡住,那么该方法假设是回溯,直到情况不再如此,然后用“F”标记该路径,以表示坏路径。 现在我不明白为什么

  • 我的任务是用回溯和递归的方法解决一个迷宫。这更多的是一个关于这个概念的概念问题。 回溯电话是如何接通的?从我所看到的所有示例来看,似乎递归总是在回溯步骤之前立即调用,所以回溯是无法实现的。谁能给我解释一下回溯步骤是怎么达到的?

  • 我在用递归解迷宫。我的矩阵是这样的 这是更大矩阵的原型。我的求解递归方法如下所示 你们可以注意到,这里我返回一个布尔值,如果我找到一条路径,它应该会给我一个真值。但它总是给我错误的答案。我不确定我在递归方法中犯的逻辑错误。方法如下 endX=3;endY=10;