递归算法中的两大经典问题(八皇后&汉诺塔)
1.八皇后问题
八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。
解析:
要解决此问题,首先我们得先知道判断皇后不能相互攻击的条件是什么?
1.每个皇后的上下左右及其夹角的八个方向都不能有对应的皇后
2.每行都得放一个皇后所以对于八皇后问题,我有如下的解法思路供参考:
1.首先创建一个8X8 的数组,默认为0表示该位置没有皇后,1表示该位置有皇后。
2.定义辅助函数进行递归,加入一系列的判断条件算出结果;
1.最好将数组备份,便于操作
2.判断某个位置是否可以放皇后?
只需要判断该位置的左上,上,右上三个方向没有皇后即可,然后在递归让row+1使该行只有一个皇后。
3.如何判断某位置的对应处有黄后?
假设i为行数,j为列数
左上:arr[i-?][j-?]==1 (i>=0;j>=0)
正上:arr[i-?][j]==1 (i>=0)
右上:arr[i-?][j+?]==1 (i>=0;j<8)
3.实现辅助函数的细节;
现在上代码:
public class EightQueue{
public static void main(String[] args){
private static int count=0;
int [][] arr= new int[8][8];//将此处的数字改成输入N,然后将下面的所有相关数据修改即可以实现N皇后问题
eightQueue(0,arr);
System.out.println("总共有"+count+"种可能!");
}
private void eightQueue(int row,int[][] arr){
if(row==8){ //row从0-7,当row==8则可知该种情况下可以放完八个皇后
count++;
}else{
int[][]newArr=new int[8][8]; //备份数组便于操作
for(int i=0;i<8;i++){
for(int j=0;j<8;j++){
newArr[i][j]=arr[i][j];
}
}
for(int col=0;col<8;col++){
for(int j=0;j<8;j++){ //先清空该行存的皇后,即让所有元素都等于一
newArr[row][j]=0;
}
if(nodangerous(row,col,newArr)){ //判断其三个方向有没有皇后
newArr[row][col]=1;
eightQueue(row+1,newArr); //递归调用让row+1判断下一行
}
}
}
private boolean nodangerous(int row,int col,int [][] arr){
for(int i=row-1;i>=0;i--) {
if(newArr[i][col]==1) {
return false;
}
}
//左上方
for(int i=row-1,j=col-1;i>=0&&j>=0;i--,j--) {
if(newArr[i][j]==1) {
return false;
}
}
//右上方
for(int i=row-1,j=col+1;i>=0&&j<8;i--,j++) {
if(newArr[i][j]==1) {
return false;
}
}
//否则
return true;
}
}
}
}
2.汉诺塔问题
相传在古印度圣庙中,有一种被称为汉诺塔(Hanoi)的游戏。该游戏是在一块铜板装置上,有三根杆(编号A、B、C),在A杆自下而上、由大到小按顺序放置64个金盘(如下图)。游戏的目标:把A杆上的金盘全部移到C杆上,并仍保持原有顺序叠好。操作规则:每次只能移动一个盘子,并且在移动过程中三根杆上都始终保持大盘在下,小盘在上,操作过程中盘子可以置于A、B、C任一杆上。
分析:对于这样一个问题,任何人都不可能直接写出移动盘子的每一步,但我们可以利用下面的方法来解决。设移动盘子数为n,为了将这n个盘子从A杆移动到C杆,可以做以下三步:
(1)以C盘为中介,从A杆将1至n-1号盘移至B杆;
(2)将A杆中剩下的第n号盘移至C杆;
(3)以A杆为中介;从B杆将1至n-1号盘移至C杆。
这里我们可以利用栈来实现盘子在每个杆子上的增删问题:
1.设A,B,C三个杆子分别为from,mid,to
注意:
如果从A->C 则B为中介杆,若剩下最后一个盘子肯定是从A->C
如果从B->C 则A为中介杆,若剩下最后一个盘子肯定是从B->C
如果从A->B 则C为中介杆,若剩下最后一个盘子肯定是从A->B
public class Hanover {
public static void main(String[] args) {
LinkedStack<Integer> from=new LinkedStack<>();
LinkedStack<Integer> mid=new LinkedStack<>();
LinkedStack<Integer> to=new LinkedStack<>();
for(int i=0;i<4;i++) {
from.push(i);
}
move2(from,mid,to);
}
private static void move2(LinkedStack<Integer> from, LinkedStack<Integer> mid, LinkedStack<Integer> to, int level) {
if(level==1) { //剩下最后一个盘子时
System.out.println(from+"->"+to);
}else {
move2(from,to,mid,level-1); //第一步 从A-B利用C做中介
System.out.println(from+"->"+to); //打印路径
move2(mid,from,to,level-1); //第二步 从B-C 利用A做中介
}
}