332.重新安排行程
给定一个机票的字符串二维数组 [from, to],子数组中的两个成员分别表示飞机出发和降落的机场地点,对该行程进行重新规划排序。所有这些机票都属于一个从 JFK(肯尼迪国际机场)出发的先生,所以该行程必须从 JFK 开始。
说明:
- 如果存在多种有效的行程,你可以按字符自然排序返回最小的行程组合。例如,行程 [“JFK”, “LGA”] 与 [“JFK”, “LGB”] 相比就更小,排序更靠前
- 所有的机场都用三个大写字母表示(机场代码)。
- 假定所有机票至少存在一种合理的行程。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/reconstruct-itinerary
/**
* 算法:回溯+贪心
* 思想:① 以起点城市建立终点城市的邻接表
* ② 对相同起点城市的机票按字典序排序(贪心),这样我们每次取邻接点,取到的肯定是行程最短的
* ③ 因为我们找到的一条行程(欧拉路径)就是行程最短的路径,所以就可以直接回溯,不需要再继续找了,因此可以标记一个isFind,当ifFind=true 直接return
* ④ dfs中cnt来计数使用的机票数,n表示总机票数,beg表示起点城市,adjMap就是我们先前创建的邻接表
* ⑤ 遍历邻接表 -> 删除机票 -> dfs -> 恢复机票
*/
List<String> tmp;
List<String> ans;
boolean isFind = false;
public List<String> findItinerary(List<List<String>> tickets) {
//check
if (tickets == null || tickets.size() == 0) return new ArrayList<>();
tmp = new ArrayList<>();
//adj
Map<String, List<String>> adjMap = new HashMap<>();
for (int i = 0;i < tickets.size();i++){
//邻接表
List<String> adj = adjMap.getOrDefault(tickets.get(i).get(0),new LinkedList<>());
adj.add(tickets.get(i).get(1));
adjMap.put(tickets.get(i).get(0),adj);
}
//sort (贪心算法,按照字典序排序,使得每次选择的都是字典序最小的行程)
Set<String> keys = adjMap.keySet();
Iterator<String> iterator = keys.iterator();
while (iterator.hasNext()){
Collections.sort(adjMap.get(iterator.next()),((o1, o2) -> o1.compareTo(o2)));//lambda
}
//dfs
tmp.add("JFK");
dfs(adjMap,"JFK",0,tickets.size());
//ans
return ans;
}
private void dfs(Map<String, List<String>> adjMap,String beg,int cnt,int n){
if(cnt >= n){
ans = new ArrayList<>(tmp);
isFind = true;//标记,找到最小行程就可以直接回溯了
return;
}
List<String> list = adjMap.get(beg);
if (list == null || list.size() == 0) return;//这里很重要,防止空指针异常
for (int i = 0;i < list.size();i++){
String des = list.get(0);
tmp.add(des);
list.remove(des);
//dfs
dfs(adjMap, des, cnt + 1, n);
if (isFind) return;//剪枝
//回溯
tmp.remove(tmp.size()-1);//这里是关键,要删除后面的元素,我之前运行错误就是因为我删了前面的des
list.add(des);
}
}