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、递归函数参数
定义全局变量二维数组result来记录最终结果。
参数n是棋盘的大小,然后用row来记录当前遍历到棋盘的第几层了。
2、递归终止条件
当递归到棋盘最底层(也就是叶子节点)的时候,就可以收集结果并返回了
3、单层搜索的逻辑
递归深度就是row控制棋盘的行,每一层里for循环的col控制棋盘的列,一行一列,确定了放置皇后的位置。
每次都是要从新的一行的起始位置开始搜,所以col都是从0开始。
- 验证棋盘是否合法
- 不能同行
- 不能同列
- 不能同斜线 (45度和135度角)
例如:n=3,没有符合条件的棋盘
注意:要把数组转换为字符串
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,'.');
}
backtracking(n,0,chessboard);
return result;
}
public void backtracking(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';
backtracking(n,row+1,chessboard);//下一层
chessboard[row][col]='.';//回溯
}
}
}
public List Array2List(char[][] chessboard){//数组转换字符串
List<String> path=new ArrayList<>();
for (char[] c:chessboard){//每一行
path.add(String.copyValueOf(c));//copyValueOf返回char参数的字符串
}
return path;
}
public boolean isValid(int row,int col,int n,char[][] chessboard){//检查是否合法
//检查列
for(int i=0;i<row;i++){
if (chessboard[i][col]=='Q'){
return false;
}
}
for (int i=0;i<col;i++){
if (chessboard[row][i]=='Q'){
return false;
}
}
//检查左对角线
for (int i=row-1,j=col-1;i>=0&&j>=0;i--,j--){
if (chessboard[i][j]=='Q'){
return false;
}
}
//检查右对角线
for (int i=row-1,j=col+1;i>=0&&j<n;i--,j++){
if (chessboard[i][j]=='Q'){
return false;
}
}
return true;
}
}
37.解数独
编写一个程序,通过填充空格来解决数独问题。
数独的解法需 遵循如下规则:
- 数字
1-9
在每一行只能出现一次。 - 数字
1-9
在每一列只能出现一次。 - 数字
1-9
在每一个以粗实线分隔的3x3
宫内只能出现一次。(请参考示例图)
数独部分空格内已填入了数字,空白格用 '.'
表示。
示例 1:
输入:board = [["5","3",".",".","7",".",".",".","."],["6",".",".","1","9","5",".",".","."],[".","9","8",".",".",".",".","6","."],["8",".",".",".","6",".",".",".","3"],["4",".",".","8",".","3",".",".","1"],["7",".",".",".","2",".",".",".","6"],[".","6",".",".",".",".","2","8","."],[".",".",".","4","1","9",".",".","5"],[".",".",".",".","8",".",".","7","9"]]
输出:[["5","3","4","6","7","8","9","1","2"],["6","7","2","1","9","5","3","4","8"],["1","9","8","3","4","2","5","6","7"],["8","5","9","7","6","1","4","2","3"],["4","2","6","8","5","3","7","9","1"],["7","1","3","9","2","4","8","5","6"],["9","6","1","5","3","7","2","8","4"],["2","8","7","4","1","9","6","3","5"],["3","4","5","2","8","6","1","7","9"]]
解释:输入的数独如上图所示,唯一有效的解决方案如下所示:
问题分析:
1、递归函数以及参数
找整个树用void,找单个树枝boolean。因为解数独找到一个符合的条件(就在树的叶子节点上)立刻就返回,相当于找从根节点到叶子节点一条唯一路径,所以需要使用boolean返回值。
2、递归终止条件
本题递归不用终止条件,解数独是要遍历整个树形结构寻找可能的叶子节点就立刻返回。其终止条件其实在单层搜索逻辑里。
3、递归单层搜索逻辑
需要两个for循环,二维递归。一个for循环遍历棋盘的行,一个for循环遍历棋盘的列,一行一列确定下来之后,递归遍历这个位置放9个数字的可能性。
如果找到合适的位置就return true,若9个数都试完了,没有一个合法,那就return false,这也就是为什么没有终止条件,也不会永远填不满棋盘而无限递归下去。
到最后遍历完如果没有false,就返回true。
判断棋盘是否合法有如下三个维度:
- 同行是否重复
- 同列是否重复
- 9宫格里是否重复
class Solution {
public void solveSudoku(char[][] board) {//无需定义result集合,因为返回类型是void,直接改数组的值
backtracking(board);
}
public boolean backtracking(char[][] board){
//无需终止条件,最后在单层搜索的逻辑会返回
for (int i=0;i<9;i++){//遍历行
for (int j=0;j<9;j++){//遍历列
if (board[i][j]!='.'){
continue;
}
if (board[i][j]=='.'){
for (char k='1';k<='9';k++){//判断放置数字1-9是否可行
if (isValid(i,j,k,board)){//判断是否合法
board[i][j]=k;
boolean result=backtracking(board);//假设放1之后开始递归,
//数独上在放1之后所有放的数都有妥善的位置,
//就返回ture,此时整个数独的放置都结束了
if (result==true){//只要最后找到一种数独结果就返回
return true;
}
/* if (backtracking(board)){
return true;
}*/
board[i][j]='.';//回溯退回
}
}
//9个数放在这个位置都不行,就返回false
return false;//这也是不需要终止条件的原因
}
}
}
//遍历完没有false
return true;
}
public boolean isValid(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;
}
}
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;
}
}