LeetCode 51. N皇后
题目链接:https://leetcode.cn/problems/n-queens/description/
文章链接:https://programmercarl.com/0051.N%E7%9A%87%E5%90%8E.html
思路
从图中,可以看出,二维矩阵中矩阵的高就是这棵树的高度,矩阵的宽就是树形结构中每一个节点的宽度。
我依然是定义全局变量二维数组result来记录最终结果。
参数n是棋盘的大小,然后用row来记录当前遍历到棋盘的第几层了。
当我们遍历到棋盘最最底层就可以收集结果了,即row == n。
递归深度就是row控制棋盘的行,每一层里for循环的col控制棋盘的列,一行一列,确定了放置皇后的位置。
每次都是要从新的一行的起始位置开始搜,所以都是从0开始。
List<List<String>> res = 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 res;
}
private void backtracking(int n, int row, char[][] chessboard) {
// 当遍历到叶子节点的时候,收集结果
if (row == n) {
res.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] = '.';
}
}
}
private 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;
}
// 检查45°对角线,因为我们现在需要将Q放在row,col的位置,因此我们应该沿着这个位置向
// 左上角检查,即从row-1,row-1开始,这里注意我们不需要向右下角检查是因为棋盘是由第一层向最后一层放的,所有右下角不可能有元素
for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
if (chessboard[i][j] == 'Q')
return false;
}
// 检查135°对角线,因为我们现在需要将Q放在row,col的位置,因此我们应该沿着这个位置向
// 右上角检查,即从row-1,col+1开始
for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
if (chessboard[i][j] == 'Q')
return false;
}
return true;
}
private List Array2List(char[][] chessboard) {
ArrayList<String> list = new ArrayList<>();
for (char[] c : chessboard) {
list.add(String.copyValueOf(c));
}
return list;
}
LeetCode 37. 解数独
题目链接:https://leetcode.cn/problems/sudoku-solver/description/
文章链接:https://programmercarl.com/0037.%E8%A7%A3%E6%95%B0%E7%8B%AC.html#%E7%AE%97%E6%B3%95%E5%85%AC%E5%BC%80%E8%AF%BE
思路
本题中棋盘的每一个位置都要放一个数字(而N皇后是一行只放一个皇后),并检查数字是否合法,解数独的树形结构要比N皇后更宽更深。
递归函数的返回值需要是bool类型,为什么呢?
因为解数独找到一个符合的条件(就在树的叶子节点上)立刻就返回,相当于找从根节点到叶子节点一条唯一路径,所以需要使用bool返回值
递归的作用就是在每个点上放上数字。
public void solveSudoku(char[][] board) {
backtracking(board);
}
private boolean backtracking(char[][] board) {
// 一个for循环遍历棋盘的行,一个for循环遍历棋盘的列
// 一行一列确定下来之后,递归遍历这个位置放9个数字的可能
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
if (board[i][j] != '.')
continue;
// 遍历1-9的所有可能
for (char k = '1'; k <= '9'; k++) {
if (isValidSudoku(i,j,k,board)){
board[i][j] = k;
// 如果找到一组合适的就立刻返回
if (backtracking(board)) {
return true;
}
board[i][j] = '.';
}
}
// 9个数字试完了,都不行,返回false
return false;
}
}
// 遍历完都没有返回false,说明找打了合适位置
return true;
}
/**
* 判断棋盘是否合法有如下三个维度:
* 同行是否重复
* 同列是否重复
* 9宫格里是否重复
*/
private 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;
}