JAVA-数据结构与算法-递归

420 阅读5分钟

写在前面

递归

  • 方法自己调用自己,每次调用时传入不同的变量
  • 调用机制,依次入栈执行,最终可以打破自己调用自己的循环,一个个出栈(后进先出)

注意事项

  • 执行一个方法时,就创建一个受保护的独立空间(栈空间);
  • 方法的局部变量是独立的,不会相互影响;
  • 如果方法中使用的是引用类型的变量,就会共享该引用类型变量(如数组);
  • 递归必须向推出递归的条件逼近,否则就是无限递归,出现异常StackOverFlowError
  • 当一个方法执行完毕,遇到return放回,遵守谁调用,将结果返回给谁,方法执行完毕,这层方法也出栈

迷宫回溯问题

  • 走出迷宫的最短路径
  • 通过假设的方式,不断向下一个点尝试,直到有返回值后,依次倒序回溯,影响最初的点
  • 例如A点有四个方位,每个方位尝试过后,再将A自身的结果返回给A的上一个,而A的尝试中,也包括A的下一个的尝试,不断包含
  • 每一个点都在自己这个位置进行四个方向的尝试,并影响上一个点
/**
 * 规则 i,j 表示起点 6,5表示终点
 * mazeArr[i][j]为0 表示没有走过 为1为墙 为2可以走 为3已经走过,但是走不通
 * 在走迷宫时,要确定策略,下->右->上->左;如果改点走不通就回溯
 * @param mazeArr 地图
 * @param i 从哪个位置开始找
 * @param j
 * @return 如果找到返回true
 */
public static boolean getWay(int[][] mazeArr, int i, int j) {
    if(mazeArr[6][5] == 2) {
        return true;
    } else {
        if (mazeArr[i][j] == 0) {
            //按照策略走 下->右->上->左
            //假定该点可以走,如果不能走通,后续置3
            mazeArr[i][j] = 2;
            if (getWay(mazeArr, i + 1, j)) {
                //向下走
                return true;
            } else if (getWay(mazeArr, i, j + 1)) {
                //向右走
                return true;
            } else if (getWay(mazeArr, i - 1, j)) {
                //向上走
                return true;
            } else if (getWay(mazeArr, i, j - 1)) {
                //向左走
                return true;
            } else {
                //该点走不通,是死路
                mazeArr[i][j] = 3;
                return false;
            }
        } else { //mazeArr[i][j] !=0
            //1 表示围墙,不能走
            //2 表示是走过的,那就返回false,让上一个点选择别的路
            //3 表示死路
            return false;
        }
    }
}

最短路径

  • 通过调整策略,实际上有4*4=16种策略,下面的笨方法只写了四组试试看
  • 调用,countAndShowAndRecover方法里面实现了记录2的个数、地图输出和还原;将每个策略2的个数,存到ArrayList,然后排个序输出最小的,也可以存在map更好
//通过4*8的数组决定四种策略
// 10 01 -10 0-1 下 右 上 左
int[] up = new int[]{-1, 0};
int[] down = new int[]{1, 0};
int[] right = new int[]{0, 1};
int[] left = new int[]{0, -1};
//上下右左
List<int[]> strategy1 = Arrays.asList(up, down, right, left);
//下上左右
List<int[]> strategy2 = Arrays.asList(down, up, left, right);
//左上右下
List<int[]> strategy3 = Arrays.asList(left, up, right, down);
//右下上左
List<int[]> strategy4 = Arrays.asList(right, down, up, left);
HashMap<String, List<int[]>> strategies = new HashMap<>();
strategies.put("strategy1", strategy1);
strategies.put("strategy2", strategy2);
strategies.put("strategy3", strategy3);
strategies.put("strategy4", strategy4);
Set<String> set = strategies.keySet();
ArrayList<Integer> counts = new ArrayList<>();
for (String s : set) {
    List<int[]> strategy = strategies.get(s);
    System.out.println("--------" + s + "---------");
    getWay(mazeArr, 1,1, strategy);
    counts.add(countAndShowAndRecover(mazeArr));
}
counts.sort((o1, o2) -> o1 - o2);
System.out.println("4个策略的最短路径: " + counts.get(0));
  • getWay方法修改
public static boolean getWay(int[][] mazeArr, int i, int j, List<int[]> strategy) {
        ....
        if (mazeArr[i][j] == 0) {
            //按照策略走 下->右->上->左
            //假定该点可以走,如果不能走通,后续置3
            mazeArr[i][j] = 2;
            if (getWay(mazeArr, i + strategy.get(0)[0], j + strategy.get(0)[1], strategy)) {
                return true;
            } else if (getWay(mazeArr, i+ strategy.get(1)[0], j + strategy.get(1)[1], strategy)) {
                return true;
            } else if (getWay(mazeArr, i + strategy.get(2)[0], j+ strategy.get(2)[1], strategy)) {
                return true;
            } else if (getWay(mazeArr, i+ strategy.get(3)[0], j + strategy.get(3)[1], strategy)) {
                return true;
            }
            ....
}

八皇后问题

  • 8*8的方格上,任意两个皇后不能在同一列,同一行,同一斜线 image.png
  • 每次递归都改变当前一个皇后的位置,再来决定下面的皇后位置;
  • 比如说1 2 3 4要排列组合,先固定1再固定2,修改3 4的位置;固定1再固定3修改2 4的位置···,只不过八皇后问题有额外的要求
  • 调用,从第0个皇后开始
eq.check(0);
  • 放置方法
//放置第n个皇后开始
//每一行的皇后都要有8次的尝试 也就是 8^8
private void check(int n) {
    if (n == max) {
        //n == max = 8 因为n是0~7代表已经放好
        print();
        return;
    }
    for (int i = 0; i < max; i++) {
        //先把当前n这个皇后放在第i列
        arr[n] = i;
        //判断放置n这个皇后是否冲突
        if (judge(n)) {
            //不冲突,放n+1个皇后
            check(n+1);
            //当n = 7 开始返回的时候,n = 6开始i++,尝试下面的可能性
            //当返回到 n=1时,n=1的i++,开始下一次的循环,直到n=1走完max这个循环
        }
        //如果冲突,就放在下一个i+1列,直到放到正确的位置
    }
    //check退出的条件只有n=8或者走完max循环
    //走完循环
    return;
}
  • 条件检测
//查看当我们放置第n个皇后,就去检测该皇后是否和前面已经摆放的皇后冲突
//n表示判断第n个皇后
private boolean judge(int n) {
    //是否在同一行不用判断,因为i永远小于n
    for (int i = 0; i < n; i++) {
        if (arr[i] == arr[n] //同一列
            // 是否在同一斜线,由于棋盘是正方形,同一条斜线,是一次函数,|x1 - x2| = |y1 - y2|
            || Math.abs(n-i) == Math.abs(arr[n] - arr[i])) {
            return false;
        }
    }
    return true;
}
- 输出结果
private void print() {
    count ++;
    for (int i : arr) {
        System.out.print(i + " ");
    }
    System.out.println();
}