leetcode 51. N-Queens

一 题目

The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two queens attack each other.

Given an integer n, return all distinct solutions to the n-queens puzzle.

Each solution contains a distinct board configuration of the n-queens' placement, where 'Q' and '.' both indicate a queen and an empty space respectively.

Example:

Input: 4
Output: [
 [".Q..",  // Solution 1
  "...Q",
  "Q...",
  "..Q."],

 ["..Q.",  // Solution 2
  "Q...",
  "...Q",
  ".Q.."]
]
Explanation: There exist two distinct solutions to the 4-queens puzzle as shown above.

Accepted 159,582  Submissions  384,320

二 分析

  hard 级别,N皇后问题。我记得上学的时候学过8皇后的问题,不过早都忘了,应该是经典的算法问题。

国际象棋的皇后的攻击规则:就是行、列、对角线(正反)。不能有重复的,否则就会攻击到。上面说了限制规则,目标就是在N*N的棋盘上放上N个皇后,互相不攻击。

这个题目,跟之前的数独问题: leetcode 37. Sudoku Solver 从思路上很类似的。就是没有想到直观的解决办法,要去不断的尝试,数独是我在一个位置尝试填入一个数,满足我就填下一个,不满足我尝试别的数。这个就是我尝试在一个位置放入皇后,满足方一个,不满足把原来放入皇后移走。尝试下一个位置。适合递归的思路,深度优先。完成就加入到结果。

        依然没有做出来,后来看了网上大神的文章,才理清思路。

1 算法开始, 初始化N*N棋盘,填入“.”占位。

2. 从0行开始调用递归函数。

3 递归函数:

   3.1我们首先判断当前行数是否已经为n,是的话说明所有的皇后都已经成功放置好了,所以要把 queens 数组加入结果 res 中即可。

3.2 否则 我们遍历该行的所有列的位置,行跟列的位置都确定后, 在当前行,当前列的位置上判断是否满足条件(即保证经过这一点的行,列与斜线上都没有两个皇后)

       如果满足:先标识位Q(放入皇后),在递归下一行。

     不满足条件的情形:

  • 将当前行当前列的位置回溯,置为未放状态,再接着判断当前行下一列,目的为了遍历所有的解决方案。

       其中校验函数需要对比画图看。因为是逐行去填充而每行只有一个皇后,且在数组中只占据一个元素的位置,行冲突就不存在了。只看剩余的列冲突、正反对角线冲突就好。这里要注意找规律。代码里还有个剪枝优化,就是不用判断一整个线的数据,值从当前的queens[i,j]开始向上找,减少了判断。

还是看代码直观些。

//n皇后问题。
	//规则:横、竖、斜线上不能有重复的。
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		List<List<String>> res =solveNQueens(4);
		System.out.println(JSON.toJSON( res));
	}
	
	//递归
	public static List<List<String>> solveNQueens(int n) {
		 List<List<String>> res = new ArrayList();
		 //初始化
		 char[][] queens= new char[n][n];		 
		 //填充
		 for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
            	queens[i][j]='.';
            }
	     }
		 helper(0,queens,res);
		 return res;
    }


	private static void helper(int row, char[][] queens, List<List<String>> res) {
		//符合条件,放满了
		if(row== queens.length ){
			 List<String> ls=new ArrayList<String>();
			 for(int i=0;i<queens.length;i++){
				 ls.add(String.valueOf(queens[i]));				
			 }
			 res.add(ls);
			return;
		}
		//遍历所有列		
		for(int col=0;col<queens.length;col++ ){
			//check:是否可在row行j列处放Q
			if(isValid(queens ,row,col)){
				//先标记
				queens[row][col]='Q';
				//尝试下一行
				helper(row+1,queens, res);
				//不匹配再清除
				queens[row][col]='.';
			}
		}
	}
	//rule:限制规则:(按行处理的,所以不在检查行)
	static boolean isValid( char[][] queens, int row,int col ){
		//check 列
		for(int i=0;i<row;i++){
			if(queens[i][col]=='Q'){
				return false;
			}
		}
		//check 斜线/:
		for(int i=row-1,j=col+1;i>=0&&j<queens.length;i--,j++){
			if(queens[i][j]=='Q'){
				return false;
			}
		}
		//check 反斜线\:只查到上半截
		for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
	        if (queens[i][j]=='Q') {
	            return false;
	        }
	    }
		return true;
	}

Runtime: 2 ms, faster than 95.36% of Java online submissions for N-Queens.

Memory Usage: 37.9 MB, less than 75.68% of Java online submissions for N-Queens.

时间复杂度O(N!).

 

算法珠玑上还介绍一种只使用一个一维数组 queenCol 来保存所有皇后的列位置。这个很牛逼,因为二维数组是更接近我们正常思维的,压缩到一维数组,整个大逻辑不变的情况下,匹配成功的情况下,加入到结果集需要转换下,校验规则都要变。这里大神很厉害,判断两个点是否在对角线上,用了二点的横坐标差的绝对值等于纵坐标差的绝对值,666.大神总是碾压式存在。

 

class Solution {
    public List<List<String>> solveNQueens(int n) {
          List<List<String>> res = new ArrayList();
		 int[] queens = new int[n];
		 //初始化-1
		 for(int i=0;i<n;i++){
			 queens[i] =-1;
		 }
		 helper(0,queens,res);
		 return res;
	}
	
	private static void helper(int row, int[] queens, List<List<String>> res) {
		//符合条件,放满了
		if(row== queens.length ){
			List<String> ls = new ArrayList();
			//转换为结果
			for(int i=0;i<queens.length;i++ ){				
				 char[] charArray = new char[queens.length];				 
				for(int j=0;j<queens.length;j++){
					if(j==queens[i]){
						charArray[j] ='Q';
					}else{
						charArray[j] ='.';
					}
				}
				ls.add(String.valueOf(charArray));
			}
			res.add(ls);
		}
		//遍历所有列		
		for(int col=0;col<queens.length;col++ ){
			//check:是否可在row行j列处放Q,不满足就尝试下一列
			if(isValid(queens ,row,col)){
				queens[row]= col;
				helper(row+1,queens,res );
				queens[row]= -1;
			}
		}
		
	}
	//判断当前位置能否放Q
	static boolean isValid(int[] queens, int row,int col ){
		
		for(int i=0;i<row;i++){
			//check 列
			if(queens[i]==col){
				return false;
			}
			//判断对角线:横坐标差的绝对值等于纵坐标差的绝对值
			if (Math.abs(i - row) == Math.abs(queens[i] - col)) return false;
		}	
		
		return true;
	}
	
}

Runtime: 2 ms, faster than 95.36% of Java online submissions for N-Queens.

Memory Usage: 36.2 MB, less than 100.00% of Java online submissions for N-Queens.

理论上会更快,我觉得实际跑出来结果一样。我觉得是受累于拼装结果那个字符串了。

参考:

https://soulmachine.gitbooks.io/algorithm-essentials/java/dfs/n-queens.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值