我想在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;
}
有人对我有所暗示吗?
答案 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)
添加一些额外的东西以简化操作:
start
和goal
是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)并且它应该可以正常工作
这个解决方案不是一个完美而有效的解决方案,但我已经尝试使代码更易于阅读和理解。我认为这种递归问题值得高度重视。