递归地在二维数组上找到路径[Java]

时间:2018-01-03 09:08:10

标签: java recursion

我想在java中编写一个程序,它在带有递归的二维数组上找到一个路径。 名为“gitter”的2d数组由“字段”类型的对象组成。 每个字段将使用100-999范围内的随机数进行初始化。如果一个字段用数字初始化,其中一个数字是素数,那么它就是一个“陷阱”。这些字段可以想象为正方形,因此我只能在4个方向上移动:

public class Field {

int number;
boolean visited;

Field() {
    Random rn = new Random();
    this.number = rn.nextInt((999+1) - 100) + 100;
    this.visited = false;
}

boolean isTrap() {
    String str = String.valueOf(number);
    if(str.contains("2") | str.contains("3") | str.contains("5") | str.contains("7")) {
        return true;
    } return false;
  }    
}

路径的起始字段和结束字段的曼哈顿距离应大于2.目前的问题是递归。我想的越多,其中的案例就越来越长。我还添加了一个布尔变量“visited”,因为两次没有访问同一个字段,但没有成功。递归需要while循环吗?如果不是(我猜),在找到路径后,最简单的方法是使递归停止?我尝试使用和不使用while循环,但找不到任何解决方案。

public class Gitter {

Field[][] gitter = new Field[10][10];
List<Field> path = new ArrayList<Field>();

public Field[] getStartAndGoal() {
    boolean notFound = true;
    Field[] startGoal = new Field[2];
    while(notFound) {   
        Random x0 = new Random();
        Random y0 = new Random();
        Random x1 = new Random();
        Random y1 = new Random();
        int row0 = x0.nextInt((9)+1);
        int line0 = y0.nextInt((9)+1);
        int row1 = x1.nextInt((9)+1);
        int line1 = y1.nextInt((9)+1);
        int distance = Math.abs(row1-row0) + Math.abs(line1-line0);
        if(distance>2){
            if(gitter[row0][line0].isTrap() == false && gitter[row1][line1].isTrap() ==false) {
                notFound = false;
            Field start = gitter[row0][line0];
            Field goal = gitter[row1][line1];
            startGoal[0] = start;
            startGoal[1] = goal;
            }                                              
        }
    }
   return startGoal; 
}

public boolean findPath(Field start, Field goal) {


    boolean solved = false;

    for(int i = 0; i < 10; i++) {
        for(int j = 0; j < 10; j++) {
            if(gitter[i][j].equals(start)) {
                gitter[i][j].visited=true;                            
                while(solved==false){
                    if((i+1)<10 && gitter[i+1][j].isTrap()==false && gitter[i+1][j].visited == false && findPath(gitter[i+1][j], goal)){
                        gitter[i+1][j].visited = true;
                        path.add(gitter[i+1][j]);
                        return true;
                    }
                    if((i-1)>0 && gitter[i-1][j].isTrap()==false && gitter[i-1][j].visited == false && findPath(gitter[i-1][j], goal)){
                        gitter[i-1][j].visited = true;
                        path.add(gitter[i-1][j]);
                        return true;
                    }
                    if((j+1)<10 && gitter[i][j+1].isTrap()==false && gitter[i][j+1].visited == false && findPath(gitter[i][j+1], goal)){
                        gitter[i][j+1].visited = true;
                        path.add(gitter[i][j+1]);
                        return true;
                    }
                    if((j-1)>10 && gitter[i][j-1].isTrap()==false && gitter[i][j-1].visited == false && findPath(gitter[i][j-1], goal)){
                        gitter[i][j-1].visited = true;
                        path.add(gitter[i][j-1]);
                        return true;
                    }
                    for(i=0; i<path.size(); i++) {
                        if(path.get(i).equals(goal)){
                            solved = true;
                            break;                                                                                               
                        }
                    }
                }                                      
            }
        }
    } return false;      
}

有人对我有所暗示吗?

2 个答案:

答案 0 :(得分:0)

看看这个问题,有许多变量不一定太重要,不能考虑,但我可以更快地创建一个简单的解决方案。如果您使用我的解决方案中的概念,请记下完全忽略的各种约束,这些约束可以轻松实现。

递归

通常,递归用于替换循环的迭代行为。出现此问题时,如果存在可以将您从A点移动到B点的路径,或者直到您确定存在路径,则需要搜索每个可能的路径。

  

我想的越多,其中的案例就越来越长

易。创建一个辅助方法,为您进行计算,以避免重复使用相同的代码。

  

递归需要while循环吗?

我不完全确定你在循环中做了什么,但是对于这个解决方案来说,完全取消循环肯定是可能的(而且更加优雅)。

  

在找到路径后,最简单的方法是使递归停止吗?

找到解决方案后,返回true。之前的递归步骤将获得一个真正的值,然后理解你已经到达终点的某个位置,所以它也应该返回true。这就是递归方面可以产生如此优雅的解决方案的地方。

解决方案

    public static void main (String args[]) {
        int n = 10;
        int[][] map = new int[n][n]; // Generate random data however you want
        boolean[][] visited = new boolean[n][n]; // Defaults to false
        int[2] start = new int[]{0,0}; // Top left corner
        int[2] end = new int[]{n-1, n-1}; // Bottom right corner

        boolean path = pathExists(map, visited, start, end);
    }

    public static boolean pathExists(int[][] map, boolean[][] visited, int[] current, int[] end) {
        int x = current[0];
        int y = current[1];
        // If not in bounds, not a valid route
        if (!inBounds(map, visited, current)) return false;
        // If this is the end, return true!
        if (current[0] == end[0] && current[1] == end[1]) return true;

        // Attempt each cardinal direction. If you find a return, return true
        if (pathExists(map, markPresent(visited, current), new int[]{x-1,y}, end)) return true;
        if (pathExists(map, markPresent(visited, current), new int[]{x+1,y}, end)) return true;
        if (pathExists(map, markPresent(visited, current), new int[]{x,y+1}, end)) return true;
        if (pathExists(map, markPresent(visited, current), new int[]{x,y-1}, end)) return true;
        // There is no solution down this path    
        return false;
    }

    public static boolean[][] markPresent(boolean[][] visited, int[] current) {
        // Make a deep copy - Is needed to prevent interferance
        boolean[][] copy = new boolean[visited.length][visited[0].length];
        for (int i = 0; i < copy.length; i++)
            copy[i] = Arrays.copyOf(visited[i], visited[i].length);
        // Mark where you currently are
        copy[current[0]][current[1]] = true;
        return copy;
    }

    public static boolean inBounds(int[][] map, boolean[][] visited, int[] position) {
        int x = position[0];
        int y = position[1];
        // Make sure it is within the bounds of the map
        if (x < 0 || y < 0 || x >= map.length || y >= map.length) return false;
        // Check if the current block is a barrier
        if (isBarrier(map, position)) return false;
        // If you have visited this path before, don't do it again
        if (visited[x][y]) return false;

        // Otherwise, check the path!
        return true;
    }

    public static boolean isBarrier(int[][] map, int[] position) {
        // Return your definition of a barrier (I used modulo 10 for testing
    }

我做过最低限度的测试,所以如果你能看到任何有趣的问题,请随时发表评论。

从我可以收集到的内容中,您想要检测点之间是否存在路径。如果您想要检测最短距离(使用Manhatten指标),返回布尔值的实例可以返回整数值。如果要查找路径本身,可以返回一个点数组,并递归地将所有点附加到另一个数组。这是一个找到最短路径的修改版本:

public static ArrayList<int[]> findDistance(int[][] map, boolean[][] visited, int[] current, int[] end) {
    int x = current[0];
    int y = current[1];
    ArrayList<int[]> ret = new ArrayList<>();
    ret.add(current);

    if (!inBounds(map, visited, current)) return new ArrayList<>(); // Return empty array
    if (current[0] == end[0] && current[1] == end[1]) return ret; // Return current location

    ArrayList<ArrayList<int[]>> paths = new ArrayList<>();
    paths.add(findDistance(map, markPresent(visited, current), new int[]{x-1,y}, end));
    paths.add(findDistance(map, markPresent(visited, current), new int[]{x+1,y}, end));
    paths.add(findDistance(map, markPresent(visited, current), new int[]{x,y+1}, end));
    paths.add(findDistance(map, markPresent(visited, current), new int[]{x,y-1}, end));

    // Find the shortest path that leads somewhere
    paths.removeIf((ArrayList<int[]> data) -> data.size() == 0);
    paths.sort(Comparator.comparing(ArrayList::size));

    // If the size of the paths is 0, no path was found
    if (paths.size() == 0) {
        return new ArrayList<>();
    } else {
        //Append the found path to the current location and return the list
        ret.addAll(paths.get(0));
        return ret;
    }
}

修改

我有一种可怕的感觉,我忘记了一些明显的东西 - 我有。您将需要克隆受访阵列,以便不干扰每个递归步骤。我添加了一种方法来做到这一点。

答案 1 :(得分:-1)

添加一些额外的东西以简化操作:

  • startgoal是Gitter类的属性
  • findPath已被分为方法,一个是公共的,另一个是私人的

代码

public class Gitter {
    Field[][] gitter = new Field[10][10];
    List<Field> path = new ArrayList<Field>();

    private Field start = null;
    private Field goal = null;

    // i've omited initializing too

    public boolean findPath(Field start, Field goal) {
        this.start = start;
        this.goal = goal;
        // Instead of having one recursive method, I divided it in two, one public and another private
        for(int i = 0; i < 10; i++) {
            for(int j = 0; j < 10; j++) {
                if(gitter[i][j].equals(start)) {
                    return findPath(i,j);
                }
            }
        } return false;      
    }

    /*
     * Check if available path exist from Field [i][j]
     */
    private boolean findPath(int i, int j){

        boolean solved = false;

        // This check makes if-else chain cleaner 
        if(i < 0 || i >= 10 || j < 0 || j >= 10){
            return false;
        }
        // Don't check already visited cells
        if(!gitter[i][j].visited){
            gitter[i][j].visited=true;

            // If its possible to have a trap in goal Field, this check must be first
            if(gitter[i][j].equals(goal)){
                path.add(gitter[i][j]);
                return true;
            }

            // If start Field mustn't be a trap, first condition should be removed
            if(!gitter[i][j].equals(start) && gitter[i][j].isTrap()){
                return false;
            }

            // Down
            if(findPath(i+1,j)){
                solved = true;
            }
            // Up
            else if(findPath(i-1,j)){
                solved = true;
            }
            // Right
            else if(findPath(i,j+1)){
                solved = true;
            }
            // Left
            else if(findPath(i,j-1)){
                solved = true;
            }

            // If any direction check found a path, this cell is part of that path
            if(solved){
                path.add(gitter[i][j]);
            }

            return solved;
        }
        return false;
    }
}

我尝试过使用较小的数字(从1到9)并且它应该可以正常工作

这个解决方案不是一个完美而有效的解决方案,但我已经尝试使代码更易于阅读和理解。我认为这种递归问题值得高度重视。