You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java递归实现二维数组路径查找问题求助

递归路径查找问题的解决思路

嘿,我来帮你捋捋这个递归路径查找的问题~你的核心困扰主要在递归逻辑混乱、不必要的while循环,以及递归终止的处理上,咱们一步步拆解:

1. 递归里完全不需要while循环!

这是你当前代码最核心的问题之一。递归本身就是通过函数调用栈实现深度优先遍历+回溯的,while循环会让当前节点反复检查四个方向,不仅打乱递归的自然流程,还可能导致死循环或者重复处理,完全是画蛇添足。

2. 递归的终止条件要明确

递归的终止逻辑其实很清晰,分两种情况:

  • 成功终止:当前节点就是目标终点,直接返回true,同时把当前节点加入路径
  • 失败终止:当前节点超出数组边界、是陷阱、已经被访问过,直接返回false

3. 现有代码的几个关键bug

先帮你指出几个明显的错误,这些也是导致你逻辑混乱的原因:

  • 边界判断错误:比如(i-1)>0应该是(i-1)>=0(不然第一行的节点无法向上移动),(j-1)>10完全写错了,应该是(j-1)>=0
  • 访问标记时机不对:应该在进入当前节点时就标记为visited,而不是在递归调用之后
  • 路径收集顺序错误:递归是深度优先,找到终点后会从终点往回收集节点,最后需要反转路径才能得到从起点到终点的顺序
  • 查找节点坐标的效率低:每次递归都遍历整个数组找start的位置,不如直接把坐标(row, col)作为递归参数传入,既高效又避免了equals判断的潜在问题(你的Field类没重写equals,默认是引用比较,虽然当前场景可能没问题,但传坐标更直接)

4. 重构后的递归思路示例

给你一个简化的、可运行的递归实现思路,核心是用坐标代替Field对象作为参数:

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

    // 初始化二维数组
    public Gitter() {
        for (int i = 0; i < 10; i++) {
            for (int j = 0; j < 10; j++) {
                gitter[i][j] = new Field();
            }
        }
    }

    // 修改getStartAndGoal,返回起点和终点的坐标(这里用数组返回四个int:startRow, startCol, goalRow, goalCol)
    public int[] getStartAndGoalCoords() {
        boolean notFound = true;
        Random rn = new Random(); // 只创建一个Random实例更高效
        int[] coords = new int[4];
        while (notFound) {
            int row0 = rn.nextInt(10);
            int line0 = rn.nextInt(10);
            int row1 = rn.nextInt(10);
            int line1 = rn.nextInt(10);
            int distance = Math.abs(row1 - row0) + Math.abs(line1 - line0);
            if (distance > 2 && !gitter[row0][line0].isTrap() && !gitter[row1][line1].isTrap()) {
                coords[0] = row0;
                coords[1] = line0;
                coords[2] = row1;
                coords[3] = line1;
                notFound = false;
            }
        }
        return coords;
    }

    // 递归查找路径的核心方法,参数是当前坐标和目标坐标
    public boolean findPath(int currentRow, int currentCol, int goalRow, int goalCol) {
        // 失败终止条件:越界、是陷阱、已访问
        if (currentRow < 0 || currentRow >= 10 || currentCol < 0 || currentCol >= 10
                || gitter[currentRow][currentCol].isTrap()
                || gitter[currentRow][currentCol].visited) {
            return false;
        }

        // 成功终止条件:到达终点
        if (currentRow == goalRow && currentCol == goalCol) {
            path.add(gitter[currentRow][currentCol]);
            return true;
        }

        // 标记当前节点为已访问,避免重复走
        gitter[currentRow][currentCol].visited = true;

        // 尝试四个方向:下、上、右、左(顺序不影响,只要覆盖四个方向即可)
        if (findPath(currentRow + 1, currentCol, goalRow, goalCol)
                || findPath(currentRow - 1, currentCol, goalRow, goalCol)
                || findPath(currentRow, currentCol + 1, goalRow, goalCol)
                || findPath(currentRow, currentCol - 1, goalRow, goalCol)) {
            // 如果某个方向找到了路径,把当前节点加入路径(回溯时收集)
            path.add(gitter[currentRow][currentCol]);
            return true;
        }

        // 可选:如果需要多次查找路径,这里要取消标记(回溯),否则可以省略
        // gitter[currentRow][currentCol].visited = false;
        return false;
    }

    // 测试用的方法
    public static void main(String[] args) {
        Gitter g = new Gitter();
        int[] coords = g.getStartAndGoalCoords();
        boolean found = g.findPath(coords[0], coords[1], coords[2], coords[3]);
        if (found) {
            // 因为路径是从终点到起点收集的,反转后得到起点到终点的顺序
            Collections.reverse(g.path);
            System.out.println("找到路径啦!");
            for (Field f : g.path) {
                System.out.print(f.number + " -> ");
            }
        } else {
            System.out.println("没有找到可行路径");
        }
    }
}

// 你的Field类可以保留,建议重写equals和hashCode
class Field {
    int number;
    boolean visited;
    Random rn = new Random(); // 可以把Random实例提升为成员变量,避免每次创建

    Field() {
        this.number = rn.nextInt(900) + 100; // 简化写法:100-999的随机数
        this.visited = false;
    }

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

    // 可选:重写equals和hashCode
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Field field = (Field) o;
        return number == field.number;
    }

    @Override
    public int hashCode() {
        return Objects.hash(number);
    }
}

5. 关键逻辑说明

  • 递归流程:每次进入一个节点,先判断是否符合终止条件,符合就返回;否则标记为已访问,尝试四个方向的递归调用。如果某个方向返回true,说明该方向能到终点,就把当前节点加入路径,再返回true给上一层。
  • 路径反转:因为递归是从终点往回收集节点,所以最后需要用Collections.reverse(path)来得到从起点到终点的正确顺序。
  • 回溯标记:如果需要多次查找不同路径,记得在四个方向都失败后,把当前节点的visited改回false,这样下次查找时还能访问这个节点。

6. 其他小优化

  • Field类里的Random可以提升为成员变量,不用每次创建新实例,更高效
  • getStartAndGoal里的Random也只需要创建一个
  • 边界判断统一写成currentRow >=0 && currentRow <10,更清晰易懂

按照这个思路调整后,你的递归逻辑会变得清晰很多,也能正确终止递归并收集路径啦~

内容的提问来源于stack exchange,提问作者jo87casi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:15:59