【综合算法学习】(第十二篇)
目录
N皇后(hard)
题目解析
讲解算法原理
编写代码
有效的数独(medium)
题目解析
讲解算法原理
编写代码
N皇后(hard)
题目解析
1.题目链接:. - 力扣(LeetCode)
2.题目描述
按照国际象棋的规则,皇后可以攻击与之处在同⼀⾏或同⼀列或同⼀斜线上的棋⼦。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
讲解算法原理
解法:
算法思路:
⾸先,我们在第⼀⾏放置第⼀个皇后,然后遍历棋盘的第⼆⾏,在可⾏的位置放置第⼆个皇后,然后再遍历第三⾏,在可⾏的位置放置第三个皇后,以此类推,直到放置了n个皇后为⽌。
我们需要⽤⼀个数组来记录每⼀⾏放置的皇后的列数。在每⼀⾏中,我们尝试放置⼀个皇后,并检查是否会和前⾯已经放置的皇后冲突。如果没有冲突,我们就继续递归地放置下⼀⾏的皇后,直到所有的皇后都放置完毕,然后把这个⽅案记录下来。
在检查皇后是否冲突时,我们可以⽤⼀个数组来记录每⼀列是否已经放置了皇后,并检查当前要放置的皇后是否会和已经放置的皇后冲突。对于对⻆线,我们可以⽤两个数组来记录从左上⻆到右下⻆的每⼀条对⻆线上是否已经放置了皇后,以及从右上⻆到左下⻆的每⼀条对⻆线上是否已经放置了皇后。
• 对于对⻆线是否冲突的判断可以通过以下流程解决:
1. 从左上到右下:相同对⻆线的⾏列之差相同;
2. 从右上到左下:相同对⻆线的⾏列之和相同。
因此,我们需要创建⽤于存储解决⽅案的⼆维字符串数组 solutions ,⽤于存储每个皇后的位置的⼀维整数数组 queens ,以及⽤于记录每⼀列和对⻆线上是否已经有皇后的布尔型数组
columns 、 diagonals1 和 diagonals2 。
递归函数设计:voiddfs(vector<vector<string>>&solutions,vector<int>&queens,int&n,introw,vector<bool>&columns,vector<bool>&diagonals1,vector<bool>&diagonals2)
参数:row(当前需要处理的⾏数);
返回值:⽆;
函数作⽤:在当前⾏放⼊⼀个不发⽣冲突的皇后,查找所有可⾏的⽅案使得放置n个皇后后不发⽣冲突。
递归函数流程如下:
1. 结束条件:如果 row 等于 n ,则表⽰已经找到⼀组解决⽅案,此时将每个皇后的位置存储到字符串数组 board 中,并将 board 存储到 solutions 数组中,然后返回;
2. 枚举当前⾏的每⼀列,判断该列、两个对⻆线上是否已经有皇后:
a. 如果有皇后,则继续枚举下⼀列;
b. 否则,在该位置放置皇后,并将 columns 、 diagonals1 和 diagonals2 对应的位置
设为 true ,表⽰该列和对⻆线上已经有皇后:
i. 递归调⽤ dfs 函数,搜索下⼀⾏的皇后位置。如果该⽅案递归结束,则在回溯时需要将
columns 、 diagonals1 和 diagonals2 对应的位置设为 false ,然后继续枚举
下⼀列;
编写代码
c++算法代码:
class Solution
{bool checkCol[10], checkDig1[20], checkDig2[20];vector<vector<string>> ret;vector<string> path;int n;
public:vector<vector<string>> solveNQueens(int _n) {n = _n;path.resize(n);for(int i = 0; i < n; i++)path[i].append(n, '.');dfs(0);return ret;}void dfs(int row){if(row == n){ret.push_back(path);return;}for(int col = 0; col < n; col++) // 尝试在这⼀⾏放皇后{// 剪枝if(!checkCol[col] && !checkDig1[row - col + n] && !checkDig2[row +
col]){path[row][col] = 'Q';checkCol[col] = checkDig1[row - col + n] = checkDig2[row +
col] = true;dfs(row + 1);// 恢复现场path[row][col] = '.';checkCol[col] = checkDig1[row - col + n] = checkDig2[row +
col] = false;}}}
};
java算法代码:
class Solution
{boolean[] checkCol, checkDig1, checkDig2;List<List<String>> ret;char[][] path;int n;public List<List<String>> solveNQueens(int _n) {n = _n;checkCol = new boolean[n];checkDig1 = new boolean[n * 2];checkDig2 = new boolean[n * 2];ret = new ArrayList<>();path = new char[n][n];for(int i = 0; i < n; i++)Arrays.fill(path[i], '.');dfs(0);return ret;}public void dfs(int row){if(row == n){// 添加结果List<String> tmp = new ArrayList<>();for(int i = 0; i < n; i++){tmp.add(new String(path[i]));}ret.add(new ArrayList<>(tmp));}for(int col = 0; col < n; col++){// 判断能不能放if(checkCol[col] == false && checkDig1[row - col + n] == false &&
checkDig2[row + col] == false){path[row][col] = 'Q';checkCol[col] = checkDig1[row - col + n] = checkDig2[row +
col] = true;dfs(row + 1);// 恢复现场path[row][col] = '.';checkCol[col] = checkDig1[row - col + n] = checkDig2[row +
col] = false;}}}
}
有效的数独(medium)
题目解析
1.题目链接:. - 力扣(LeetCode)
2.题目描述
请你判断⼀个9x9的数独是否有效。只需要根据以下规则,验证已经填⼊的数字是否有效即可。数字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"]]
输出:true
⽰例2:
输⼊:
board=
[["8","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"]]
输出:false
解释:除了第⼀⾏的第⼀个数字从5改为8以外,空格内其他数字均与⽰例1相同。但由于位于左上⻆的3x3宫内有两个8存在,因此这个数独是⽆效的。
提⽰:
board.length==9
board[i].length==9
board[i][j]是⼀位数字(1-9)或者'.'
讲解算法原理
解法:
算法思路:
创建三个数组标记⾏、列以及 3*3 ⼩⽅格中是否出现 1~9 之间的数字即可。
编写代码
c++算法代码:
class Solution
{bool row[9][10];bool col[9][10];bool grid[3][3][10];
public:bool isValidSudoku(vector<vector<char>>& board) {for(int i = 0; i < 9; i++)for(int j = 0; j < 9; j++){if(board[i][j] != '.'){int num = board[i][j] - '0';// 是否是有效的if(row[i][num] || col[j][num] || grid[i / 3][j / 3][num])return false;row[i][num] = col[j][num] = grid[i / 3][j / 3][num] = true;}}return true;}
};
Java算法代码:
class Solution
{boolean[][] row, col;boolean[][][] grid;public boolean isValidSudoku(char[][] board) {row = new boolean[9][10];col = new boolean[9][10];grid = new boolean[3][3][10];for(int i = 0; i < 9; i++)for(int j = 0; j < 9; j++){if(board[i][j] != '.'){int num = board[i][j] - '0';// 是否有效if(row[i][num] || col[j][num] || grid[i / 3][j / 3][num]) return false;row[i][num] = col[j][num] = grid[i / 3][j / 3][num] = true;}}return true;}
}