目录
- [0 引言](#0 引言)
- [1 重新安排行程](#1 重新安排行程)
-
- [1.1 我的解题](#1.1 我的解题)
- [1.2 更好的解法](#1.2 更好的解法)
- [2 N皇后](#2 N皇后)
-
- [2.1 我的解题](#2.1 我的解题)
- [3 解数独](#3 解数独)
-
- [3.1 我的解题](#3.1 我的解题)
- 3.2
0 引言
听说今天的题目很难,但是我呢,必须拿下。
1 重新安排行程
- 🎈 文档讲解
- 🎈 视频讲解
- 🎈 做题状态:
其实这道题就是有向图的深度有限搜索,使用递归的方式。其实也就是二叉树的前中后序遍历。
图的广度有限搜索也就对应二叉树的层次遍历,采用队列结构和while循环。
1.1 我的解题
其实就是回溯问题,只不过要想好终止条件,以及分支条件。但是我这个暴力搜索的方法超时了。
cpp
class Solution {
public:
// 这个感觉就是递归,为什么需要回溯呢,会存在多条路径所以需要回溯
void backTracing(vector<vector<string>>& tickets, vector<bool>& used, vector<string>& path, vector<vector<string>>& paths)
{
// 终止条件
if (path.size() == tickets.size() + 1)
{
paths.emplace_back(path);
return;
}
// 单层回溯逻辑
for (int i = 0; i < tickets.size(); i++)
{
// 如果当前路径没有使用过,且是满足路径的要求的话,则添加
if (used[i] == false && tickets[i][0] == path[path.size() - 1])
{
path.emplace_back(tickets[i][1]);
used[i] = true;
backTracing(tickets, used, path, paths);
path.pop_back();
used[i] = false;
}
}
}
vector<string> findItinerary(vector<vector<string>>& tickets)
{
vector<bool> used(tickets.size(), false);
vector<string> path = {"JFK"};
vector<vector<string>> paths;
backTracing(tickets, used, path, paths);
// 从paths中找出字典排序最小的结果
int minIndex = 0;
for (int i = 1; i < paths.size(); i++)
{
if (paths[i] < paths[minIndex])
{
minIndex = i;
}
}
return paths[minIndex];
}
};
1.2 更好的解法
首先得理解下面这段代码的含义。
cpp
unordered_map<string, map<string, int>> targets;
targets["JFK"]["TIS"]++;
targets["JFK"]["TIS"]++;
这行代码在做几件事情:
首先,它试图访问targets这个unordered_map的键为"JFK"的元素。如果"JFK"不存在于targets中,C++会自动创建这个键,并将其映射到一个新的(空的)内部map容器。
接着,它试图访问内部map(即"JFK"键对应的值)的键为"TIS"的元素。同样地,如果"TIS"不存在于这个内部map中,C++会自动创建这个键,并将其映射到一个int类型的值,这个值默认初始化为0(这是int类型的默认值)。
最后,++操作符会将"TIS"对应的值增加1。这意味着,我们就把从"JFK"到"TIS"的票数增加了1。
2 N皇后
- 🎈 文档讲解
- 🎈 视频讲解
- 🎈 做题状态:
2.1 我的解题
我感觉不算太难,就是比较繁琐,时间复杂度需要控制。主要是判断当前位置是否合法,也就是根据行列号,棋盘元素进行判断。回溯直接就是模板。
我一开始写的代码判断是否有皇后可以优化,因为判断是一行一行往下的,所以皇后一定是在当前行的上面才有。所以在判断斜线方向是否有皇后时,只需判断上面的行的元素即可。也就是判断45°和135°。不需要判断225°和315°这两个方向。
cpp
class Solution {
private:
vector<vector<string>> result;
// n 为输入的棋盘大小
// row 是当前递归到棋盘的第几行了
void backtracking(int n, int row, vector<string>& chessboard) {
if (row == n) {
result.push_back(chessboard);
return;
}
for (int col = 0; col < n; col++) {
if (isValid(row, col, chessboard, n)) { // 验证合法就可以放
chessboard[row][col] = 'Q'; // 放置皇后
backtracking(n, row + 1, chessboard);
chessboard[row][col] = '.'; // 回溯,撤销皇后
}
}
}
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;
}
}
// 检查 45度角是否有皇后
for (int i = row - 1, j = col - 1; i >=0 && j >= 0; i--, j--) {
if (chessboard[i][j] == 'Q') {
return false;
}
}
// 检查 135度角是否有皇后
for(int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
if (chessboard[i][j] == 'Q') {
return false;
}
}
return true;
}
public:
vector<vector<string>> solveNQueens(int n) {
result.clear();
std::vector<std::string> chessboard(n, std::string(n, '.'));
backtracking(n, 0, chessboard);
return result;
}
};
3 解数独
- 🎈 文档讲解
- 🎈 视频讲解
- 🎈 做题状态:
3.1 我的解题
解数独好像是二维的N皇后问题。需要两层循环棋盘,然后递归放入不同的数。回想之前一维数组的回溯问题,首先就是一个循环遍历数组的不同位置,然后递归。现在可以把棋盘理解为二维数组,那么也就是两个循环遍历数组位置然后递归。
cpp