递归学习-汉诺塔问题

347 阅读2分钟

“这是我参与8月更文挑战的第9天,活动详情查看:8月更文挑战

一、汉诺塔

需求:打印n层汉诺塔从最左边移动到最右边的全部过程。

要求只能小压大,不能大压小,即小的盘子能放在大的盘子上,大的盘子不能放在小的盘子上。

二、思路

先从最经典的3层汉诺塔一步一步求解。

image.png

总共3个盘子:想办法先把前2个盘子移动到中间,然后把最后一个盘子移动到最右边,然后再把中间的盘子移动到最右边。至于怎么移动,只需满足小压大规则即可,总共三步。

  1. 第一步:{1,2}盘子从左边移动到中间(左->中)
    • 第一步:{1}盘子从左移动到右边(左->右)
    • 第二步:{2}盘子从左边移动到中间(左->中)
    • 第三步:{1}盘子从右边移动到中间(右->中)
  2. 第二步:{3}盘子从左边移动到右边 (左->右)
  3. 第三步:{1,2}盘子从中间移动到右边 (中->右)
    • 第一步:{1}从中间移动到左边(中->左)
    • 第二步:{2}从中间移动到右边(中->右)
    • 第三步:{1}从左边移动到右边(左->右)

三、解答

1. 方式一

如果有N个盘子,可以推出,先把1 ~ N-1个盘子从左边移动到中间,然后第N个盘子从左边移动到右边,最后把1 ~ N-1盘子从中间移动到右边的,分成3步,每步又可以拆分为一小部分进行求解。

public static void hanoi1(int n) {
    leftToRight(n);
}

private static void leftToRight(int n) {
    if (n == 1) {
        System.out.println("Move 1 from left to right");
        return;
    }
    leftToMid(n - 1);
    System.out.println("Move " + n + " from left to right");
    midToRight(n - 1);
}

private static void leftToMid(int n) {
    if (n == 1) {
        System.out.println("Move 1 from left to mid");
        return;
    }
    leftToRight(n - 1);
    System.out.println("Move " + n + " from left to mid");
    rightToMid(n - 1);
}

private static void midToRight(int n) {
    if (n == 1) {
        System.out.println("Move 1 from mid to right");
        return;
    }
    midToLeft(n - 1);
    System.out.println("Move " + n + " from mid to right");
    leftToRight(n - 1);
}

private static void rightToMid(int n) {
    if (n == 1) {
        System.out.println("Move 1 from right to mid");
        return;
    }
    rightToLeft(n - 1);
    System.out.println("Move " + n + " from right to mid");
    leftToMid(n - 1);
}

private static void midToLeft(int n) {
    if (n == 1) {
        System.out.println("Move 1 from mid to left");
        return;
    }
    midToRight(n - 1);
    System.out.println("Move " + n + " from mid to left");
    rightToLeft(n - 1);
}

private static void rightToLeft(int n) {
    if (n == 1) {
        System.out.println("Move 1 from right to left");
        return;
    }
    rightToMid(n - 1);
    System.out.println("Move " + n + " from right to left");
    midToLeft(n - 1);
}

public static void main(String[] args) {
    hanoi1(3);
}

image.png

2. 方式二

方式一可不可以进行进一步的抽象化,通过参数控制向左移动(右->左,右->中)还是向右移动(左->右,中->右)。

public static void hanoi2(int n) {
    if (n > 0) {
        process(n, "left", "right", "mid");
    }
}

private static void process(int n, String from, String to, String other) {
    if (n == 1) {
        System.out.println("Move 1 from " + from + " to " + to);
    } else {
        process(n - 1, from, other, to);
        System.out.println("Move " + n + " from " + from + " to " + to);
        process(n - 1, other, to, from);
    }
}

public static void main(String[] args) {
    hanoi2(3);
}

四、总结

汉诺塔问题还可以采用非递归解决,有兴趣的同学自行了解哈!