深度优先搜索DFS | 回溯:力扣51. N 皇后

1、题目描述:

深度优先搜索DFS | 回溯:力扣51. N 皇后_第1张图片
深度优先搜索DFS | 回溯:力扣51. N 皇后_第2张图片

2、题解:

方法:深度优先搜索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;
    }
};

3、复杂度分析:

时间复杂度:O(N!),其中 N 是皇后数量。
空间复杂度:O(N^2),其中 N 是皇后数量。空间复杂度主要取决于递归调用层数、记录每行放置的皇后的列下标的数组以及三个集合,递归调用层数不会超过 N,数组的长度为 N,每个集合的元素个数都不会超过 N。

你可能感兴趣的:(LeetCode高频面试题)