51. N皇后
class Solution {
List<List<String>> result = new ArrayList<>();
public List<List<String>> solveNQueens(int n) {
char[][] chessboard = new char[n][n];
//将该棋盘内都填充为.
for(char[] c : chessboard){
Arrays.fill(c,'.');
}
//n为棋盘大小规模,0为当前遍历的棋盘的行数
solveNQueens1(n, 0, chessboard);
return result;
}
//row:行
//col:列
public void solveNQueens1(int n, int row, char[][] chessboard){
if(row == n){
//若递归到了叶子结点,则说明找到了一套摆放皇后的方案,加入结果集
result.add(Array2List(chessboard));
return;
}
//递归回溯部分,得到所有结果
for(int col = 0; col < n; ++col){
//若当前位置被检查后可放皇后
if(isValid(row, col, n, chessboard)){
chessboard[row][col] = 'Q';
//递归(递归探索下一行)
solveNQueens1(n, row + 1, chessboard);
//回溯
chessboard[row][col] = '.';
}
}
}
//二维数组转为列表
public List Array2List(char[][] chessboard){
List<String> list = new ArrayList<>();
//以行为单位将棋盘加入list
for(char[] c : chessboard){
list.add(String.copyValueOf(c));
}
return list;
}
//检查函数,检查当前位置能否放置皇后(相当于剪枝)
public boolean isValid(int row, int col, int n, char[][] chessboard){
//检查列
//当前要在row,col位置添加皇后,检查该位置合法性
//若i,col位置已经有皇后,即目标列中已经有皇后,则不符合要求
for(int i = 0; i < row; i++){
if(chessboard[i][col] == 'Q'){
return false;
}
}
//检查45°对角线(即左上)
for(int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--){
if(chessboard[i][j] == 'Q'){
return false;
}
}
//检查135°对角线(即右上)
for(int i = row - 1, j = col + 1; i >= 0 && j <= n - 1; i--, j++){
if(chessboard[i][j] == 'Q'){
return false;
}
}
return true;
}
}
37. 解数独
class Solution {
public void solveSudoku(char[][] board) {
//棋盘即9*9
solveSudoku1(board);
}
//只需要搜出来一个可行方案即可
public boolean solveSudoku1(char[][] board){
//外层for循环遍历棋盘的行,内层for循环遍历棋盘的列
for(int i = 0; i < 9; i++){
for(int j = 0; j < 9; j++){
//需要处理棋盘中的空格,跳过已有内容的格子
if(board[i][j] != '.'){
continue;
}
//递归回溯部分
//若当前格子为空格
//for循环这个空格放置的元素是1~9中的哪个
for(char k = '1'; k <= '9'; k++){
if(isValidSudoku(i, j, k, board)){
//若位置合法,则填入元素
board[i][j] = k;
//递归部分
//若一直符合条件,则会一直向下递归,递归到最后,然后true逐级返回
if(solveSudoku1(board)){
return true;
}
//回溯部分
//若通不过验证条件,则进行回溯,继续尝试后面的数字即可
board[i][j] = '.';
}
}
//1~9均试完都不可行则返回false
return false;
//若一行一列确定下来了,尝试了9个数都不可行,则说明这个棋盘找不到数独的解,直接返回
//这也是无终止条件也不会永远填不满棋盘无限递归下去的原因
}
}
//遍历完没有返回false,说明已经找到了合适的位置
return true;
}
//位置有效性判断,row/col位置
public boolean isValidSudoku(int row, int col, char val, char[][] board){
//同行判断
for(int i = 0; i < 9; i++){
if(board[row][i] == val){
return false;
}
}
//同列判断
for(int j = 0; j < 9; j++){
if(board[j][col] == val){
return false;
}
}
//9宫格重复判断
int startRow = (row / 3) * 3;
int startCol = (col / 3) * 3;
for(int i = startRow; i < startRow + 3; i++){
for(int j = startCol; j < startCol + 3; j++){
if(board[i][j] == val){
return false;
}
}
}
return true;
}
}
递归回溯返回值区别:
- 当问题需要求出所有可行解时,需要将返回值设置为void。
- 当只需要求出来问题的一个可行解路径时,将返回值设置为boolean。
- 此时在叶子结点递归找到一个可行解后,就会一直返回true,从而得到该解。