今天是第26天刷leetcode,立个flag,打卡60天。
算法挑战链接
332. 重新安排行程https://leetcode.cn/problems/reconstruct-itinerary/
第一想法
题目理解:将多个行程的ticket 用第一个行程的结束地点作为下一个行程的开始地点,将行程串联起来。要求一次ticket只能使用一次,且必须将所有ticket使用完。
这种题目就是无脑套用回溯算法。我就会根据回溯算法的三步曲来走
第一步:确定返回值和入参。返回值是 void 入参是:tickets 、 tickets的标记 和 开始的位置
void backtracking5(String start, List<List<String>> tickets, int[] index)
第二步:确定终止条件:当所有的tickets都被用完了,就可以结束了
if (resultTmp5.size() + 1 == tickets.size()) {
......
......
}
第三步:当前递归需要完成的事情:当找到合适的ticket,在结果列表中添加,将改ticket的位置置为已使用,进入下一个递归。递归完后恢复,结果列表中移除该ticket,将改ticket的位置置为未使用
for (int i = 0; i < index.length; i++) {
if (index[i] == 1) {
continue;
}
if (tickets.get(i).get(0).equals(start)) {
resultTmp5.add(tickets.get(i).get(1));
index[i] = 1;
backtracking5(tickets.get(i).get(1), tickets, index);
resultTmp5.removeLast();
index[i] = 0;
}
}
于是代码就写出来了如下:
class Solution {
LinkedList<String> resultTmp5 = new LinkedList<>();
List<String> result5 = new ArrayList<>();
public List<String> findItinerary(List<List<String>> tickets) {
resultTmp5.add("JFK");
backtracking5("JFK", tickets, new int[tickets.size()]);
return result5;
}
void backtracking5(String start, List<List<String>> tickets, int[] index) {
if (resultTmp5.size() - 1 == tickets.size()) {
result5 = getMinResult(resultTmp5, result5);
}
for (int i = 0; i < index.length; i++) {
if (index[i] == 1) {
continue;
}
if (tickets.get(i).get(0).equals(start)) {
resultTmp5.add(tickets.get(i).get(1));
index[i] = 1;
backtracking5(tickets.get(i).get(1), tickets, index);
resultTmp5.removeLast();
index[i] = 0;
}
}
}
private List<String> getMinResult(LinkedList<String> resultTmp5, List<String> result5) {
if (result5.isEmpty()) {
return new ArrayList<>(resultTmp5);
}
for (int i = 0; i < resultTmp5.size(); i++) {
if (resultTmp5.get(i).compareTo(result5.get(i)) > 0) {
return result5;
}else if (resultTmp5.get(i).compareTo(result5.get(i)) < 0) {
return new ArrayList<>(resultTmp5);
}
}
return result5;
}
}
但是并没有出现理想中的AC的结果。给力一个超出时间限制的结果。
看完代码随想录之后的想法
苦思冥想还是想不出来。好难呀。
对比了代码随想录的java代码之后,发现他的递归函数居然需要返回值?我和他的代码差就差在这里了。为啥需要返回值?
他给出的解释是:因为找到一条路径之后就需要在去遍历其他路径了。
对于这个解释我有点好奇,仔细对比之后发现,tickets在开始之前进行过一轮排序。所以如果找到了一条,那么这一条就是正确的答案了。
代码如下:
class Solution {
LinkedList<String> resultTmp5 = new LinkedList<>();
List<String> result5 = new ArrayList<>();
public List<String> findItinerary(List<List<String>> tickets) {
Collections.sort(tickets, (a, b) -> a.get(1).compareTo(b.get(1)));
resultTmp5.add("JFK");
backtracking5("JFK", tickets, new int[tickets.size()]);
return result5;
}
boolean backtracking5(String start, List<List<String>> tickets, int[] index) {
if (resultTmp5.size() -1 == tickets.size()) {
result5 = getMinResult(resultTmp5, result5);
return true;
}
for (int i = 0; i < index.length; i++) {
if (index[i] == 1) {
continue;
}
if (tickets.get(i).get(0).equals(start)) {
resultTmp5.add(tickets.get(i).get(1));
index[i] = 1;
if (backtracking5(tickets.get(i).get(1), tickets, index)) {
return true;
}
resultTmp5.removeLast();
index[i] = 0;
}
}
return false;
}
private List<String> getMinResult(LinkedList<String> resultTmp5, List<String> result5) {
if (result5.isEmpty()) {
return new ArrayList<>(resultTmp5);
}
for (int i = 0; i < resultTmp5.size(); i++) {
if (resultTmp5.get(i).compareTo(result5.get(i)) > 0) {
return result5;
}else if (resultTmp5.get(i).compareTo(result5.get(i)) < 0) {
return new ArrayList<>(resultTmp5);
}
}
return result5;
}
}
今日收获
并不是所有的回溯算法都是不需要返回值的。那哪些需要返回值,这里抄了一下其他人写的:
- 如果需要搜索整棵二叉树且不用处理递归返回值,递归函数就不要返回值。
- 如果需要搜索整棵二叉树且需要处理递归返回值,递归函数就需要返回值。
- 如果要搜索其中一条符合条件的路径,那么递归一定需要返回值,因为遇到符合条件的路径了就要及时返回。(本题的情况)