总结与思路:
一般的简单迷宫问题,类似于能否出去。可以使用“染色”的思路,一个节点被染红之后传给相邻的节点,最后如果出口节点也被染红,那么可以出去。从代码实现上看,
- 传入当前节点的信息,每次方法开始,判断是否为出口。是就可以提前退出,不是则继续向下执行。
- 建立迷宫大小的静态boolean数组,标识每个节点是否已被染色,避免死循环。
- 将本节点染色,判断是否有上下左右节点,有且未被染色,则调用本方法并传入将该节点信息(即递归调用)。
- 方法调用完毕,判断出口是否已经被染色。
复杂迷宫问题,查找最短路径等。
- 基本步骤同上,需注意几个问题。
- 考察最短路径,不同路径之间可以有一段重复。不应该再使用全局的Boolean数组。可以考虑使用List.contains()方法避免有重复的点。
- 需要保存路径时,使用回溯法。一般应使用stack,本题使用的是list,在执行下一次方法前将本节点加入list。然后一直向前探索,如果到了出口则保存下来,没到就不保存。递归方法返回时应该将本节点移除list,因为如果没到出口说明此路不通,无需保存本节点,如果到了出口,则已经加到result中了,不需要在保存本节点了。都应当移除,再从上一级节点探索。
遇到滴滴笔试题,
- import java.util.*;
- /**
- * @author XF 迷宫求路径,类似“染色”
- */
- public class Main {
- public static void main(String[] args) {
- Scanner sc = new Scanner(System.in);
- // 输入参数 测试:4 4 10 1 0 0 1 1 1 0 1 0 1 1 1 0 0 1 1
- int n = sc.nextInt();
- int m = sc.nextInt();
- int p = sc.nextInt();
- int[][] mi = new int[n][m];
- for (int i = 0; i < n; i++) {
- for (int j = 0; j < m; j++) {
- mi[i][j] = sc.nextInt();
- }
- }
- flag = new boolean[n][m];
- List result = new ArrayList<>();
- List tempList = new ArrayList<>();
- // 调用计算方法,传入参数。获得result.
- cc(0, 0, mi, result, tempList);
- // 比较消耗精力最少的
- int shortestNum = 0;
- List temp = (List) result.get(0);
- int min = (int) temp.get(temp.size() - 1);
- if (result.size() > 1) {
- for (int i = 1; i < result.size(); i++) {
- temp = (List) result.get(i);
- int tempMin = (int) temp.get(temp.size() - 1);
- if (tempMin < min) {
- min = tempMin;
- shortestNum = i;
- }
- }
- }
- List r = (List) result.get(shortestNum);
- r.remove(r.size() - 1);
- for(int i=0; i<r.size() ;i++){
- System.out.print(r.get(i)+",");
- }
- System.out.println("[0,3]");
- }
- // 辅助计算的静态变量
- static boolean[][] flag; // 为数组每个元素设置标志,避免重复到达某点造成多次计算或死循环
- static int pu; // pUsed 每个成功到达出口的序列计算消耗的体力值
- /*
- * 计算迷宫路径方法,主要使用递归调用每个节点的上下左右。上下左右节点又可以调用它的上下左右。 按消耗体力的数量及出口位置,优先顺序为上、右、下、左
- * 传入参数,形参,即引用变量,地址不变,但指向的地址的值可以变化.
- */
- public static void cc(int i, int j, int[][] mi, List result, List tempList) {
- // flag[i][j] = true;
- // //本题是考察最短路径,不同路径之间可以有一段重复。应使用list.contains()判断是否已包含此节点。
- // 不管从哪条路到了出口,新建list存入结果,并保存pu
- if (i == 0 && j == mi[0].length - 1) {
- tempList.add(pu);
- result.add(new ArrayList<>(tempList));
- tempList.remove((Integer) pu);
- }
- /*
- * up,从当前节点,考虑向上。如果上个节点有且没有使用,将上个节点作为参数传入方法。其他同理。
- * 将当前节点信息(x,y)存入list,调用完方法移除!!!非常重要!!核心!!
- * 因为不管是否到达出口,都说明已经探索完从这个节点伸出的路径了,应当移除,则可以从此节点上个节点继续探索。
- * 到达了出口,已经保存了,该移除;没到,此路不通,移除。 根据需求应当使用stack,我使用不多就用list代替了。
- */
- if (i > 0 && mi[i - 1][j] == 1
- && !tempList.contains("[" + (i - 1) + "," + j + "]")) {
- tempList.add("[" + i + "," + j + "]");
- pu += 3;
- cc(i - 1, j, mi, result, tempList);
- tempList.remove("[" + i + "," + j + "]");
- pu -= 3;
- }
- // right
- if (j < mi[0].length - 1 && mi[i][j + 1] == 1
- && !tempList.contains("[" + i + "," + (j + 1) + "]")) {
- tempList.add("[" + i + "," + j + "]");
- pu += 1;
- cc(i, j + 1, mi, result, tempList);
- tempList.remove("[" + i + "," + j + "]");
- pu -= 1;
- }
- // down
- if (i < mi.length - 1 && mi[i + 1][j] == 1
- && !tempList.contains("[" + (i + 1) + "," + j + "]")) {
- tempList.add("[" + i + "," + j + "]");
- cc(i + 1, j, mi, result, tempList);
- tempList.remove("[" + i + "," + j + "]");
- }
- // left
- if (j > 0 && mi[i][j - 1] == 1
- && !tempList.contains("[" + i + "," + (j - 1) + "]")) {
- tempList.add("[" + i + "," + j + "]");
- pu += 1;
- cc(i, j - 1, mi, result, tempList);
- tempList.remove("[" + i + "," + j + "]");
- pu -= 1;
- }
- }
- }