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"] ,但是它字典排序更大更靠后。
代码
class Solution {
//用深度优先搜索:dfs+map+优先队列
Map<String,PriorityQueue<String>> map = new HashMap<>();
List<String> res = new LinkedList<>();
public List<String> findItinerary(List<List<String>> tickets) {
//把tickets转为map,key是出发点,value是所有到达点的优先队列(按字典升序)
for(List<String> ticket : tickets){
String from = ticket.get(0); //出发点
String to = ticket.get(1); //到达点
//from不在map,new一个pq
if(!map.containsKey(from)){
PriorityQueue<String> pq = new PriorityQueue<>();
map.put(from,pq);
}
//把到达点加入from的pq队列中
map.get(from).add(to);
}
dfs("JFK"); //从JFK开始深度优先
return res;
}
public void dfs(String from){
PriorityQueue<String> pq = map.get(from); //获取JFK的到达pq队列
while(pq != null &&!pq.isEmpty()){
String to = pq.poll(); //从优先队列中取首元素,字典序最小的,poll保证了所有机票只用一次
dfs(to); //从to继续深度优先
}
res.addFirst(from); //把深度优先的元素一个个加到链表前面
}
}
总结
优先队列可以保证让我们找到字典序最小的路径。
这道题用回溯操作嵌套的map,代码会很难写,而且容易超时,用深度优先+map+优先队列是最好理解的。以下图为例。
map={ JFK,{ATL,SFO}, ATL,{JFK,SFO}, SFO,{ATL} }
首先,从JFK开始获取到它的优先队列pq={ATL,SFO},ATL是队首元素,说明ATL是JFK的字典序最近到达点,因此路径JFK-ATL。此时JFK的pq={SFO}
然后,从ATL开始获取它的优先队列pq={JFK,SFO},JFK是队首元素,说明JFK是ATL的字典序最近到达点,因此路径JFK-ATL-JFK。此时ATL的pq={SFO}
然后,从JFK开始获取它的优先队列pq={SFO},SFO是队首元素,说明SFO是JFK的字典序最近到达点,因此路径JFK-ATL-JFK-SFO。此时JFK的pq={null}
然后,从SFO开始获取它的优先队列pq={ATL},ATL是队首元素,说明ATL是STO的字典序最近到达点,因此路径JFK-ATL-JFK-SFO-ATL。此时SFO的pq={null}
然后,从ATL开始获取它的优先队列pq={SFO},SFO是队首元素,说明SFO是ATL的字典序最近到达点,因此路径JFK-ATL-JFK-SFO-ATL-SFO。此时ATL的pq={null}
然后,从SFO开始获取它的优先队列pq={},此时pq为空,路径结束。
最后就是按DFS的顺序把每一个位置点加入链表,因为是深度优先搜索,所以最后递归到的ATL位置放在后边,最早递归到的JFK位置放在最前面。
搞定!