代码随想录算法训练营第三十天|回溯算法的总结、332.重新安排日程
回溯算法的总结
- 回溯算法可以解决的问题
- 组合问题:如果是一个集合求组合,需要用startIndex,如果是多个集合,且集合之间互不影响,则不用startIndex。
去重问题:先给数组排序,然后用标记数组记录哪些数字已经被使用过。
- 排列问题:每层都是从0开始搜索,用标记数组记录哪些数据已经被使用过。
- 切割问题:切割问题难度在于怎么切割呢?终止条件是什么?如果获取切割完后的字串呢?
模拟组合来切割,终止条件是已经分割成目标段数并且分割出的字串是符合条件的,获取字串 用StringBuilder记录遍历的字串,然后toString()方法转换为字符串加到结果集。
- 子集问题:子集问题和组合问题唯一的区别就是:组合问题求的的结果在叶子节点,二子集的结果在树枝上,当然也包括叶子节点,所以代码上与组合问题的不同之处在于收集结果的位置不同。
- 棋盘问题:N皇后,解数独等
- 回溯模板:
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
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"]
题解:
代码:(超时代码)
class Solution {
List<String> res=new ArrayList<>();
List<String> path=new ArrayList<>();
public List<String> findItinerary(List<List<String>> tickets) {
boolean [] used=new boolean[tickets.size()];
Collections.sort(tickets,(a,b)->a.get(1).compareTo(b.get(1)));
path.add("JFK");
backtracking(tickets,used);
return res;
}
public boolean backtracking(List<List<String>> tickets,boolean [] used){
//终止条件 机场数比航班数多1
if(path.size()==tickets.size()+1){
res=path;
return true;
}
//for循环
for(int i=0;i<tickets.size();i++){
if(!used[i] && tickets.get(i).get(0).equals(path.getLast())){
path.add(tickets.get(i).get(1));
used[i]=true;
if(backtracking(tickets,used)){
return true;
}
used[i]=false;
path.removeLast();
}
}
return false;
}
}
优化代码:
要优化这段代码以避免超时,你可以考虑以下几点:
- 使用 HashMap 代替 ArrayList 来存储机场及其对应的航班信息。 这样可以提高查找速度,避免每次都遍历整个列表来查找目标机场的航班。
- 采用深度优先搜索(DFS)来遍历航班。 这样可以尽早找到满足条件的路径,避免不必要的遍历。
在 DFS 中使用递归或栈来实现。 递归比较直观,而栈则可以手动控制搜索顺序,适合对搜索顺序有要求的情况。在找到第一个满足条件的路径后就立即返回。 不必继续搜索其他可能的路径。
有效代码:
class Solution {
List<String> res = new ArrayList<>();
Map<String, PriorityQueue<String>> flights = new HashMap<>();
public List<String> findItinerary(List<List<String>> tickets) {
// 构建航班信息
for (List<String> ticket : tickets) {
flights.computeIfAbsent(ticket.get(0), k -> new PriorityQueue<>()).add(ticket.get(1));
}
// 从起始机场开始 DFS
dfs("JFK");
return res;
}
private void dfs(String airport) {
// 检查当前机场是否存在航班
PriorityQueue<String> destinations = flights.getOrDefault(airport, new PriorityQueue<>());
while (!destinations.isEmpty()) {
dfs(destinations.poll());
}
// 将当前机场加入结果集
res.add(0, airport);
}
}