1. 程式人生 > 其它 >51.N皇后問題-回溯法

51.N皇后問題-回溯法

51.N皇后問題-回溯法

題目

n皇后問題 研究的是如何將 n個皇后放置在 n×n 的棋盤上,並且使皇后彼此之間不能相互攻擊。

給你一個整數 n ,返回所有不同的n皇后問題 的解決方案。

每一種解法包含一個不同的n 皇后問題 的棋子放置方案,該方案中 'Q' 和 '.' 分別代表了皇后和空位。

示例 1

輸入:n = 4
輸出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
解釋:如上圖所示,4 皇后問題存在兩個不同的解法。

示例 2

輸入:n = 1
輸出:[["Q"]]

提示:

  • 1 <= n <= 9
  • 皇后彼此不能相互攻擊,也就是說:任何兩個皇后都不能處於同一條橫行、縱行或斜線上。

來源:力扣(LeetCode)
連結:https://leetcode-cn.com/problems/n-queens
著作權歸領釦網路所有。商業轉載請聯絡官方授權,非商業轉載請註明出處。

題解

N皇后問題可以看出本質也是枚舉出所有可能選擇一種符合條件的,所以採取回溯法。

圖太難畫了,在網上找了一張。當n=4的時候

遞迴的層數和迴圈次數都等於n

這裡有個問題,如何判斷是否在一條縱行上或在一條斜線上?
可以把棋盤看成二維陣列[x,y],遞迴的層數就是x,for迴圈就是y

isVal(x,y,board){
//在一條縱行上
for(int i=0;i<y;i++){
	if(board[i][x]=='Q') return false;
}
//在右斜線上
for(int i = y-1,j=x+1;i>=0&&j<=board.length-1;i--,j++){
	if(board[i][j]=='Q') return false;
}

//在左斜線上
for(int i = y-1,j=x-1;i>=0&&j>=0;i--,j--){
	if(board[i][j]=='Q') return false;
	}
return true;
}

遞迴三部曲

y:表示當前遞迴的層數
char[][] board:表示棋盤的狀態
int n:遞迴終止的時候會用上

List<List<String>> res  = new ArrayList<>();
char [] [] board = new char [n] [n]; //這裡使用的是二維陣列,最後還需要轉換成List<String>
void backtracing(int y,char[] [] path,int n)

遞迴的終止條件
遞迴的終止條件是path的長度等於n,但是這裡使用二維陣列初始化時就設定了長度所以不能使用這種判斷條件。
y表示遞迴的層數,當y==n時說明到了葉子節點。

if(y==n){
//Array2List將二維陣列轉換成List<String>
res.add(Array2List(board))
return;
}

String與char[]之間的轉換
String -> char[]
str.toCharArray() 返回一個char型別的陣列
char[] -> String 呼叫String的構造器
new String(arr) 返回一個String

Array2List將二維陣列轉換成List<String>

List<String> Array2List(char[][] arr){
	 List<String> list = new ArrayList<>();
      for(char[] s : arr){
		//['a','b','c']轉換成"abc"
		 list.add(new String(s));
		 return list;
      }
}

單層遞迴的邏輯

for每一層都是從0-n-1中進行選擇,並且代表棋盤的x位置。
如果是合法取值,則改為該位置為Q。

for(int x=0;x<n;x++){
	if(y<=0 || !isVal(x,y,board)) continue; //第一排是不需要驗證的
	board[y][x] = 'Q';
	backtracing(y+1,board,n);
	board[y][x] = '.';
}

程式碼

class Solution {
    List<List<String>> res  = new ArrayList<>();
    public List<List<String>> solveNQueens(int n) {
        char [] [] board = new char [n] [n];
         for(int i=0;i<n;i++){
          Arrays.fill(board[i],'.'); //先全部填充.
        }
        backtracing(0,board,n);
        return res;
    }
	//遞迴函式
    void backtracing(int y,char[] [] board,int n){
        if(y==n){
            res.add(Array2List(board));
            return;
        }
        for(int x=0;x<n;x++){
            if(y>0 && !isVal(x,y,board))continue;
    	    board[y][x] = 'Q';
	        backtracing(y+1,board,n);
	        board[y][x] = '.';
            }
        }
		//判斷是否合法
    boolean isVal(int x,int y,char[][] board){
        for(int i=0;i<y;i++){ //在一條線上
        	if(board[i][x]=='Q') return false;
        }

        for(int i = y-1,j=x+1;i>=0&&j<=board.length-1;i--,j++){//檢查右上角
	        if(board[i][j]=='Q') return false;
        }
		//檢查左上角
        for(int i = y-1,j=x-1;i>=0&&j>=0;i--,j--){
    	    if(board[i][j]=='Q') return false;
        }
        return true;
    }
	//二維char陣列轉換成 List<String>
    List<String> Array2List(char[][] arr){
	  List<String> list = new ArrayList<>();
      for(char[] s : arr){
          list.add(new String(s));
      }
      return list;
    }
}