332.重新安排行程
力扣题目链接(opens new window)
给定一个机票的字符串二维数组 [from, to],子数组中的两个成员分别表示飞机出发和降落的机场地点,对该行程进行重新规划排序。所有这些机票都属于一个从 JFK(肯尼迪国际机场)出发的先生,所以该行程必须从 JFK 开始。
提示:
- 如果存在多种有效的行程,请你按字符自然排序返回最小的行程组合。例如,行程 ["JFK", "LGA"] 与 ["JFK", "LGB"] 相比就更小,排序更靠前
- 所有的机场都用三个大写字母表示(机场代码)。
- 假定所有机票至少存在一种合理的行程。
- 所有的机票必须都用一次 且 只能用一次。
示例 1:
- 输入:[["MUC", "LHR"], ["JFK", "MUC"], ["SFO", "SJC"], ["LHR", "SFO"]]
- 输出:["JFK", "MUC", "LHR", "SFO", "SJC"]
示例 2:
- 输入:[["JFK","SFO"],["JFK","ATL"],["SFO","ATL"],["ATL","JFK"],["ATL","SFO"]]
- 输出:["JFK","ATL","JFK","SFO","ATL","SFO"]
- 解释:另一种有效的行程是 ["JFK","SFO","ATL","JFK","ATL","SFO"]。但是它自然排序更大更靠后
#include <vector>
#include <string>
#include <unordered_map>
#include <map>
#include <set>
#include <deque>
#include <algorithm>using namespace std;class Solution {
private:unordered_map<string, multiset<string>> graph;deque<string> itinerary;void dfs(const string& airport) {while (!graph[airport].empty()) {// 找到按字母序最小的下一个目的地auto next = graph[airport].begin();string nextAirport = *next;graph[airport].erase(next);dfs(nextAirport);}// 将当前机场加入行程itinerary.push_front(airport);}public:vector<string> findItinerary(vector<vector<string>>& tickets) {// 构建有向图for (const auto& ticket : tickets) {graph[ticket[0]].insert(ticket[1]);}// 从 "JFK" 开始深度优先搜索dfs("JFK");// 将双端队列转换为结果向量return vector<string>(itinerary.begin(), itinerary.end());}
};
太难了
51. N皇后
力扣题目链接(opens new window)
n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。
示例 1:
- 输入:n = 4
- 输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
- 解释:如上图所示,4 皇后问题存在两个不同的解法。
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;}}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;}
public:vector<vector<string>> solveNQueens(int n) {result.clear();std::vector<std::string> chessboard(n, std::string(n, '.'));backtracking(n,0, chessboard);return result;}
};
判断是不是可以放置Q:
- 检查左上 45 度对角线:
i
和j
分别表示行和列的坐标,i
向上(row - 1
到0
),j
向左(col - 1
到0
)检查。- 如果在 45 度对角线(左上方向)上发现其他皇后,则返回
false
,表示当前位置不安全。
// 检查 45度角是否有皇后for (int i = row - 1, j = col - 1; i >=0 && j >= 0; i--, j--) {if (chessboard[i][j] == 'Q') {return false;}}
- 检查右上 135 度对角线:
// 检查 135度角是否有皇后for(int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {if (chessboard[i][j] == 'Q') {return false;}}
- 同理,
i
向上,j
向右检查(col + 1
到n-1
)。 - 如果在 135 度对角线(右上方向)上发现其他皇后,则返回
false
。
- 同理,
37. 解数独
力扣题目链接(opens new window)
编写一个程序,通过填充空格来解决数独问题。
一个数独的解法需遵循如下规则: 数字 1-9 在每一行只能出现一次。 数字 1-9 在每一列只能出现一次。 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。 空白格用 '.' 表示。
一个数独。
答案被标成红色。
提示:
- 给定的数独序列只包含数字 1-9 和字符 '.' 。
- 你可以假设给定的数独只有唯一解。
- 给定数独永远是 9x9 形式的。
class Solution {
private:
bool backtracking(vector<vector<char>>& board) {for (int i = 0; i < board.size(); i++) { // 遍历行for (int j = 0; j < board[0].size(); j++) { // 遍历列if (board[i][j] == '.') {for (char k = '1'; k <= '9'; k++) { // (i, j) 这个位置放k是否合适if (isValid(i, j, k, board)) {board[i][j] = k; // 放置kif (backtracking(board)) return true; // 如果找到合适一组立刻返回当 backtracking 返回 true 时,表示当前路径已经找到一个合法解,此时可以直接返回 true,不需要再继续探索其他路径。如果填入数字 k 后的路径不可行,则递归返回 false,这意味着需要回溯,将当前填入的数字撤销(即重置为 '.'),并尝试下一个数字。board[i][j] = '.'; // 回溯,撤销k}}return false; // 9个数都试完了,都不行,那么就返回false}}}return true; // 遍历完没有返回false,说明找到了合适棋盘位置了
}
bool isValid(int row, int col, char val, vector<vector<char>>& board) {for (int i = 0; i < 9; i++) { // 判断行里是否重复if (board[row][i] == val) {return false;}}for (int j = 0; j < 9; j++) { // 判断列里是否重复if (board[j][col] == val) {return false;}}int startRow = (row / 3) * 3;int startCol = (col / 3) * 3;for (int i = startRow; i < startRow + 3; i++) { // 判断9方格里是否重复for (int j = startCol; j < startCol + 3; j++) {if (board[i][j] == val ) {return false;}}}return true;
}
public:void solveSudoku(vector<vector<char>>& board) {backtracking(board);}
};
- 因为解数独找到一个符合的条件(就在树的叶子节点上)立刻就返回,相当于找从根节点到叶子节点一条唯一路径,所以需要使用bool返回值
-
递归返回值的意义:
- 当
backtracking
返回true
时,表示当前路径已经找到一个合法解,此时可以直接返回true
,不需要再继续探索其他路径。 - 如果填入数字
k
后的路径不可行,则递归返回false
,这意味着需要回溯,将当前填入的数字撤销(即重置为'.'
),并尝试下一个数字。
- 当
-
提前终止条件:数独解法是一个唯一解,当我们找到一个符合的解时就可以立即返回,无需继续检查其他可能的填法。因此,返回
true
表示已经找到解,递归将逐层返回,最终到达最顶层,终止所有递归调用。
- `row / 3` 将 `row` 行号划分为 3x3 小方格的编号(值为 `0`, `1`, `2` 中的一个)