这题从理论的思路上并不难,但普通的回溯会超时,笔者一开始写了一个普通的回溯。
class Solution {
private:
vector<vector<string>>result;
vector<string>path;
void bt(vector<vector<string>>& tickets){
if(tickets.size()==0){
result.push_back(path);
return;
}
for(int i=0;i<tickets.size();i++){
if(path.size()==0){
if(tickets[i][0]=="JFK"){
path.push_back(tickets[i][0]);
path.push_back(tickets[i][1]);
vector<string>tmp=tickets[i];
tickets.erase(tickets.begin()+i);
bt(tickets);
tickets.insert(tickets.begin()+i,tmp);
path.pop_back();
path.pop_back();
}
}else{
if(path[path.size()-1]!=tickets[i][0]) continue;
path.push_back(tickets[i][1]);
vector<string>tmp=tickets[i];
tickets.erase(tickets.begin()+i);
bt(tickets);
tickets.insert(tickets.begin()+i,tmp);
path.pop_back();
}
}
}
public:
vector<string> findItinerary(vector<vector<string>>& tickets) {
bt(tickets);
while(result.size()>1){
for(int i=0;i<result[0].size();i++){
if(result[0][i]==result[1][i]){
continue;
}else if(result[0][i]>result[1][i]){
result.erase(result.begin());
break;
}else if(result[0][i]<result[1][i]){
result.erase(result.begin()+1);
break;
}
}
}
return result[0];
}
};
先通过普通的回溯求出所有可行路线,再对路线进行删除,直到剩下一条最小的,但是到第11个用例就超时了。笔者也尝试将删除路线的部分用于在回溯过程中剪枝,但依然超时,所以去看了随想录的解法,然后照着随想录的思路写。
class Solution {
private:
unordered_map<string,map<string,int>>map;
vector<string>path;
int pathsize=0;
bool bt(){
if(path.size()==pathsize) return true;
for(pair<const string,int>& i:map[path[path.size()-1]]){
if(i.second==0) continue;
i.second--;
path.push_back(i.first);
if(bt()) return true;
i.second++;
path.pop_back();
}
return false;
}
public:
vector<string> findItinerary(vector<vector<string>>& tickets) {
map.clear();
for(vector<string> i:tickets){
map[i[0]][i[1]]++;
}
path.push_back("JFK");
pathsize=tickets.size()+1;
bt();
return path;
}
};
随想录的解法通过建立unordered_map,将从一个机场到其他机场的可能性都包含在unordered_map下的map,这样在回溯过程中不需要遍历tickets寻找合适的票,并且map基于黑红树实现,能够自动实现排序,因此通过map存储机场到其他机场的余票,遍历过程中天然就可以排除所有字典排序不是最小的路线,找到的第一条可行路线就是最小的。