方法:深度优先搜索DFS ,回溯
数据结构和算法:从0到1系列 回溯小专题
回溯算法框架:
res = []
def backtrack(路径,选择列表):
做剪枝
if 满足结束条件:
res.append(路径)
return
for 选择 in 选择列表:
做选择
backtrack(路径,选择列表)
撤销选择
解决一个回溯问题,实际上就是一个决策树的遍历过程:
1、路径:也就是已经做出的选择。
2、选择列表:也就是你当前可以做的选择。
3、结束条件:也就是到达决策树底层,无法再做选择的条件。
思路:
先构建一个充满点的二维数组
构建三个集合,列、正对角线、反对角线
我们把每一行当做回溯函数的形参,对每一行进行回溯:
满足结束条件,就添加到结果数组res中
对该行每一个列进行判断:(选择列表)
如果满足条件(也就是满足皇后落子的条件,该列,正反对角线都没有'Q')
做出选择
向下做选择
撤销选择
调用回溯,传进去的参数是从第0行开始
返回res。
Python实现:
class Solution:
def solveNQueens(self, n: int) -> List[List[str]]:
# dfs ,回溯
board = [['.'] * n for _ in range(n)]
res = []
def isValid(row, col):
for i in range(row):#行,选择列表
for j in range(n):#列
if board[i][j] == 'Q' and (j == col or i + j == row + col or i - j == row - col):
return False
return True
def backtrack(row):
if row == n:
temp = board[:][:]
for i in range(n):
temp[i] = ''.join(temp[i])
res.append(temp[:][:])
return
for col in range(n):#列
if (isValid(row, col)):
board[row][col] = 'Q'
backtrack(row + 1)
board[row][col] = '.'
backtrack(0)
return res
优化:
class Solution:
def solveNQueens(self, n: int) -> List[List[str]]:
# dfs , 回溯
board = [['.'] * n for _ in range(n)]
cols,diag1,diag2 = set(),set(),set() #列集,记录出现过的皇后的列;正对角线集,反对角线集
res = []
def backtrack(row):
if row == n:
temp = board[:][:]
for i in range(n):
temp[i] = ''.join(temp[i])
res.append(temp[:][:])
return
for col in range(n):#列,选择列表
#如果当前点的行列对角线都没有皇后,即可选择,否则,跳过
if col not in cols and row - col not in diag1 and row + col not in diag2:
#做选择
board[row][col] = 'Q'
cols.add(col)
diag1.add(row-col)
diag2.add(row+col)
#向下选择
backtrack(row+1)
#撤销选择
board[row][col] = '.'
cols.remove(col)
diag1.remove(row-col)
diag2.remove(row+col)
backtrack(0)
return res
C++实现:
class Solution {
public:
vector<vector<string>> solveNQueens(int n) {
auto solutions = vector<vector<string>>();
auto queens = vector<int>(n, -1);
auto columns = unordered_set<int>();
auto diagonals1 = unordered_set<int>();
auto diagonals2 = unordered_set<int>();
backtrack(solutions, queens, n, 0, columns, diagonals1, diagonals2);
return solutions;
}
void backtrack(vector<vector<string>> &solutions, vector<int> &queens, int n, int row, unordered_set<int> &columns, unordered_set<int> &diagonals1, unordered_set<int> &diagonals2) {
if (row == n) {
vector<string> board = generateBoard(queens, n);
solutions.push_back(board);
} else {
for (int i = 0; i < n; i++) {
if (columns.find(i) != columns.end()) {
continue;
}
int diagonal1 = row - i;
if (diagonals1.find(diagonal1) != diagonals1.end()) {
continue;
}
int diagonal2 = row + i;
if (diagonals2.find(diagonal2) != diagonals2.end()) {
continue;
}
queens[row] = i;
columns.insert(i);
diagonals1.insert(diagonal1);
diagonals2.insert(diagonal2);
backtrack(solutions, queens, n, row + 1, columns, diagonals1, diagonals2);
queens[row] = -1;
columns.erase(i);
diagonals1.erase(diagonal1);
diagonals2.erase(diagonal2);
}
}
}
vector<string> generateBoard(vector<int> &queens, int n) {
auto board = vector<string>();
for (int i = 0; i < n; i++) {
string row = string(n, '.');
row[queens[i]] = 'Q';
board.push_back(row);
}
return board;
}
};
时间复杂度:O(N!),其中 N 是皇后数量。
空间复杂度:O(N^2),其中 N 是皇后数量。空间复杂度主要取决于递归调用层数、记录每行放置的皇后的列下标的数组以及三个集合,递归调用层数不会超过 N,数组的长度为 N,每个集合的元素个数都不会超过 N。