题目
332. 重新安排行程
困难
相关标签
深度优先搜索 图 欧回路
给你一份航线列表 tickets
,其中 tickets[i] = [fromi, toi]
表示飞机出发和降落的机场地点。请你对该行程进行重新规划排序。
所有这些机票都属于一个从 JFK
(肯尼迪国际机场)出发的先生,所以该行程必须从 JFK
开始。如果存在多种有效的行程,请你按字典排序返回最小的行程组合。
- 例如,行程
["JFK", "LGA"]
与["JFK", "LGB"]
相比就更小,排序更靠前。
假定所有机票至少存在一种合理的行程。且所有的机票 必须都用一次 且 只能用一次。
示例 1:
输入:tickets = [["MUC","LHR"],["JFK","MUC"],["SFO","SJC"],["LHR","SFO"]] 输出:["JFK","MUC","LHR","SFO","SJC"]
示例 2:
输入:tickets = [["JFK","SFO"],["JFK","ATL"],["SFO","ATL"],["ATL","JFK"],["ATL","SFO"]] 输出:["JFK","ATL","JFK","SFO","ATL","SFO"] 解释:另一种有效的行程是 ["JFK","SFO","ATL","JFK","ATL","SFO"] ,但是它字典排序更大更靠后。
提示:
1 <= tickets.length <= 300
tickets[i].length == 2
fromi.length == 3
toi.length == 3
fromi
和toi
由大写英文字母组成fromi != toi
思路和解题方法
- 定义了一个私有方法
backtracking
,用于进行航班路线的搜索。其中ticketNum
表示航班票数,result
是当前已经搜索到的路径。在搜索过程中,如果找到了一条合法的路径,直接返回 true 结束搜索。- 在搜索时,首先从
result
中最后一个机场出发,查找所有可以到达的目的地。这里采用了一个键值对嵌套的方式进行记录,其中外层的键为出发机场,内层的键为到达机场,内层的值为该机场之间可用的航班次数。因此,可以通过targets[result[result.size() - 1]]
查找到当前机场可以到达的所有目的地,并且记录了对应的航班次数。- 然后遍历所有可以到达的目的地,如果当前目的地没有被访问过(即其航班次数大于0),则将其加入到
result
中,并将该航班次数减1。同时,递归调用backtracking
进行下一步的搜索。如果找到了合法的路径,则直接返回 true。如果当前路径不是合法的路径,则进行回溯操作,将当前目的地从result
中移除,并将其航班次数加1,以便在后续的搜索中重新选择该目的地。- 最终,在主函数
findItinerary
中,首先进行了初始化操作,清空键值对列表targets
并记录映射关系。然后从起始机场 "JFK" 开始进行搜索,调用backtracking
函数,直至找到一条合法的路径。最后返回最终的结果。
复杂度
时间复杂度:
O(N * N^N)
时间复杂度:
- 首先,对于构建映射关系的循环操作,需要遍历全部的航班票数量,因此时间复杂度为 O(N),其中 N 是航班票的数量。
- 其次,在
backtracking
方法中,遍历所有可以到达的目的地,每个目的地都可能进行递归调用,最多进行 N 次递归。因此,在整个搜索过程中,每个节点的扩展次数加起来约为 N,所以时间复杂度为 O(N^N)。 综合起来,整体的时间复杂度为 O(N * N^N)。
空间复杂度
O(N)
空间复杂度:
- 首先,使用了一个哈希表
targets
来存储航班的映射关系,其中需要记录出发机场、到达机场和航班次数。因此,该哈希表所占用的空间为 O(N),其中 N 是航班票的数量。- 其次,在递归调用
backtracking
过程中,需要维护一个路径result
,其长度不会超过航班票数量 N。因此,路径所占用的空间为 O(N)。 综合起来,整体的空间复杂度为 O(N)。
c++ 代码
class Solution {
private:
unordered_map<string, map<string, int>> targets; // 存储出发机场和到达机场之间的航班次数
/**
* 回溯函数,用于生成航班路径
* @param ticketNum 总的航班数量
* @param result 当前正在生成的航班路径
* @return 是否成功找到完整的航班路径
*/
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;
}
public:
/**
* 寻找符合条件的航班路径
* @param tickets 给定的航班信息(包含起始机场和到达机场)
* @return 符合条件的航班路径
*/
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; // 返回符合条件的航班路径
}
};
觉得有用的话可以点点赞,支持一下。
如果愿意的话关注一下。会对你有更多的帮助。
每天都会不定时更新哦 >人< 。