private: // row: 当前层数 // col, diagonals1, diagonals2: 分别记录列、主对角线\、副对角线/的被攻击状态 (1代表被攻击,0代表安全) voidbacktrack(vector<vector<string>>& res, vector<int>& queens, int n, int row, int col, int diagonals1, int diagonals2){ if (row == n) { res.push_back(generateBoard(queens, n)); return; }
// 获取当前行所有可以放置皇后的位置 (将位设为 1 代表可以放置) int available = ((1 << n) - 1) & ~(col | diagonals1 | diagonals2);
// 只要还有可以放置的位置,就不断提取出来尝试 while (available != 0) { // 提取出最低位的 1 作为当前要放置的位置 int position = available & (-available); // 将刚提取的 1 从 available 中消除 available &= (available - 1); // 计算这个位对应的列索引 (末尾 0 的个数) int colIndex = __builtin_ctz(position); queens[row] = colIndex;
// 递归进入下一行: // 列限制增加 position // diagonals1(主对角线\)限制增加 position 右移,对应数字的位运算是左移一位 // diagonals2(副对角线/)限制增加 position 左移,对应数字的位运算是右移一位 backtrack(res, queens, n, row + 1, col | position, (diagonals1 | position) >> 1, (diagonals2 | position) << 1); } }
// 辅助函数:根据 queens 数组生成字符串棋盘 vector<string> generateBoard(const vector<int>& queens, int n){ vector<string> board(n, string(n, '.')); for (int i = 0; i < n; i++) { board[i][queens[i]] = 'Q'; } return board; } };
📊 复杂度分析
1、基于集合的回溯
时间复杂度:O(N!),其中 N 是皇后数量。
空间复杂度:O(N),其中 N 是皇后数量。空间复杂度主要取决于递归调用层数、记录每行放置的皇后的列下标的数组以及三个集合,递归调用层数不会超过 N,数组的长度为 N,每个集合的元素个数都不会超过 N。
2、基于位运算的回溯
时间复杂度:O(N!),其中 N 是皇后数量。
空间复杂度:O(N),其中 N 是皇后数量。由于使用位运算表示,因此存储皇后信息的空间复杂度是 O(1),空间复杂度主要取决于递归调用层数和记录每行放置的皇后的列下标的数组,递归调用层数不会超过 N,数组的长度为 N。