今天的三道题重点掌握N皇后,二刷看其他两道。
332.重新安排行程(hard)
一刷搞清楚几个问题就行,容器的使用这有点麻烦。
- 一个行程中,如果航班处理不好容易变成一个圈,成为死循环。 先遍历记录所有机场能够走的次数,利用次数>0避免循环,如果“航班次数”大于零,说明目的地还可以飞,如果“航班次数”等于零说明目的地不能飞了,而不用对集合做删除元素或者增加元素的操作。
- 有多种解法,字母序靠前排在前面,让很多同学望而退步,如何该记录映射关系呢 使用map记录顺序
- 使用回溯法(也可以说深搜) 的话,那么终止条件是什么呢?结果集的大小 = 机票数量 + 1 代表所机票涵盖的有机场(可能有重复)都走过一遍了
- 搜索的过程中,如何遍历一个机场所对应的所有机场。 使用unorderedmap
class Solution {
public:
unordered_map<string, map<string, int>> targets;
vector<string> findItinerary(vector<vector<string>>& tickets) {
targets.clear();
vector<string> result;
for (const vector<string>& vec : tickets) {
targets[vec[0]][vec[1]]++;
}
result.push_back("JFK");
BackTracking(tickets.size(), result);
return result;
}
bool BackTracking(int ticketNum, vector<string>& result) {
if (result.size() == ticketNum + 1) {
return true;
}
for (pair<const string, int>& target : targets[result[result.size() - 1]]) {
if (target.second > 0) {
result.push_back(target.first);
target.second--;
if (BackTracking(ticketNum, result)) return true;
result.pop_back();
target.second++;
}
}
return false;
}
};
N皇后
棋盘的宽度就是for循环的长度,递归的深度就是棋盘的高度,主要难点在回溯处理的是二维的棋盘了。
- 注意递归行不用判断重复,因为每次只在每一行放一个,需要判断同列,左上对角,右上对角是否有重复。
自己按照回溯的思路写出来的,太棒了。
//C++
class Solution {
public:
vector<vector<string>> res;
vector<vector<string>> solveNQueens(int n) {
res.clear();
vector<string> chessboard(n, string(n, '.'));
BackTracking(n, 0, chessboard);
return res;
}
void BackTracking(int n, int row, vector<string>& chessboard) {
if (row == n) {
res.push_back(chessboard);
return;
}
for (int i = 0; i < n; i++) {
if (isValid(row, i, chessboard, n)) {
chessboard[row][i] = 'Q';
BackTracking(n, row + 1, chessboard);
chessboard[row][i] = '.';
}
}
}
bool isValid(int row, int col, vector<string>& chessboard, int n) {
for (int i = 0; i < row; i++) {
if (chessboard[i][col] == 'Q') {
return false;
}
}
for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
if (chessboard[i][j] == 'Q') {
return false;
}
}
for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
if (chessboard[i][j] == 'Q') {
return false;
}
}
return true;
}
};
C# 的List不像vector容器可以初始化和指定默认值,Fill的方式还得把string转成char数组,直接charchar记录棋盘,最后还得用StringBulider转回sting的List,注意C#里二维交错数组要单独new分配空间。
- 判断是否有皇后对角线要从点出发向左上右上检查,不能从两个头检查,当前点不一定就在正对角线上。
//C#
public class Solution {
IList<IList<string>> res = new List<IList<string>>();
public IList<IList<string>> SolveNQueens(int n) {
//这是交错数组
char[][] board = new char[n][];
for (int i = 0; i < n; i++) {
board[i] = new char[n];
}
foreach(char[] item in board) {
Array.Fill(item, '.');
}
BackTracking(0, n, board);
return res;
}
public List<string> ToIList(char[][] board){
List<string> list = new List<string>();
StringBuilder sb = new StringBuilder();
foreach(char[] chess in board){
foreach(var ch in chess)
sb.Append(ch);
list.Add(sb.ToString());
sb.Clear();
}
return list;
}
public void BackTracking(int row, int n, char[][] chessboard) {
if (row == n) {
res.Add(ToIList(chessboard));
return;
}
for (int col = 0; col < n; col++) {
if (isValid(row, col, n, chessboard)) {
chessboard[row][col] = 'Q';
BackTracking(row + 1, n, chessboard);
chessboard[row][col] = '.';
}
}
}
public bool isValid(int row, int col, int n, char[][] chessboard) {
for (int i = 0; i < row; i++) {
if (chessboard[i][col] == 'Q') return false;
}
for (int i = row - 1, j = col - 1; i >= 0 && j >=0; i--, j--) {
if (chessboard[i][j] == 'Q') return false;
}
for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
if (chessboard[i][j] == 'Q') return false;
}
//for (int i = 0, j = 0; i < row && j < col; i++, j++) {
// if (chessboard[i][j] == 'Q') return false;
//}
//for (int i = n - 1, j = n - 1; i < row && j > col; i++, j--) {
// if (chessboard[i][j] == 'Q') return false;
//}
return true;
}
}
37. 解数独
二维递归,二刷再看,合理条件和二维递归的思想是核心。