递归算法中的两大经典问题(八皇后&汉诺塔)

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做中介
		}
	}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值