N皇后
其实是全排列的变种
回溯算法的模板:
result = []
def backtrack(路径, 选择列表):
if 满足结束条件:
result.add(路径)
return
for 选择 in 选择列表:
做选择
backtrack(路径, 选择列表)
撤销选择
class Solution {
List<List<String>> res = new ArrayList<>();
/* 输入棋盘边长 n,返回所有合法的放置 */
public List<List<String>> solveNQueens(int n) {
// '.' 表示空,'Q' 表示皇后,初始化空棋盘
List<String> board = new ArrayList<>();
for (int i = 0; i < n; i++) {
StringBuilder sb = new StringBuilder();
for (int j = 0; j < n; j++) {
sb.append('.');
}
board.add(sb.toString());
}
backtrack(board, 0);
return res;
}
// 路径:board 中小于 row 的那些行都已经成功放置了皇后
// 选择列表:第 row 行的所有列都是放置皇后的选择
// 结束条件:row 超过 board 的最后一行
void backtrack(List<String> board, int row) {
// 触发结束条件
if (row == board.size()) {
res.add(new ArrayList<>(board));
return;
}
int n = board.get(row).length();
for (int col = 0; col < n; col++) {
// 排除不合法选择
if (!isValid(board, row, col)) {
continue;
}
// 做选择
StringBuilder sb = new StringBuilder(board.get(row));
sb.setCharAt(col, 'Q');
board.set(row, sb.toString());
// 进入下一行决策
backtrack(board, row + 1);
// 撤销选择
sb.setCharAt(col, '.');
board.set(row, sb.toString());
}
}
/* 是否可以在 board[row][col] 放置皇后? */
boolean isValid(List<String> board, int row, int col) {
int n = board.size();
/* 检查列是否有皇后互相冲突 */
for (int i = 0; i < n; i++) {
if (board.get(i).charAt(col) == 'Q') {
return false;
}
}
/* 检查右上方是否有皇后互相冲突 */
for (int i = row - 1, j = col + 1;
i >= 0 && j < n; i--, j++) {
if (board.get(i).charAt(j) == 'Q') {
return false;
}
}
/* 检查左上方是否有皇后互相冲突 */
for (int i = row - 1, j = col - 1;
i >= 0 && j >= 0; i--, j--) {
if (board.get(i).charAt(j) == 'Q') {
return false;
}
}
return true;
}
}
全排列
class Solution {
List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> permute(int[] nums) {
//记录路径
LinkedList<Integer> track = new LinkedList<>();
//路径中的元素会被标记为true,避免重复使用
boolean[] used = new boolean[nums.length];
backtrack(nums,track,used);
return res;
}
void backtrack(int[] nums,LinkedList track,boolean[] used){
//触发结条件
if(track.size() == nums.length){
res.add(new LinkedList(track));
return;
}
for (int i = 0; i < nums.length ; i++){
if(used[i]){
//如果已经在路径中,则跳过
continue;
}
//做选择
track.add(nums[i]);
used[i] = true;
//进入下一层决策树
backtrack(nums,track,used);
//取消选择
track.removeLast();
used[i] = false;
}
}
}