方法:深度优先搜索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 resC++实现:
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。