0. 难点
- 一个行程中,如果航班处理不好容易变成一个圈,成为死循环
- 有多种解法,字母序靠前排在前面,让很多同学望而退步,如何该记录映射关系呢 ?
- 使用回溯法(也可以说深搜) 的话,那么终止条件是什么呢?
- 搜索的过程中,如何遍历一个机场所对应的所有机场。
1. 初次写40min,没有什么方法论…
一开始选的数据结构不好,后面就直接陷进去了。直接按照常规的邻接表写了一个unordered_map<string, vector<string>>
对难点的处理:
- 一个行程中,如果航班处理不好容易变成一个圈,成为死循环。
- 进入递归的时候,把已经选过的航班删除了!后面在恢复。一层回溯就 o ( n 2 ) o(n^2) o(n2) 了
- 有多种解法,字母序靠前排在前面,让很多同学望而退步,如何该记录映射关系呢 ?
- 我直接在记录完的时候对
unordered_map<string, vector<string>>
中的邻接表vector<string>
排序一下。o(n^2 * logn)
- 我直接在记录完的时候对
- 使用回溯法(也可以说深搜) 的话,那么终止条件是什么呢?
- 正确处理,path的size是机票的size+1。
- 搜索的过程中,如何遍历一个机场所对应的所有机场。
- 在我的数据结构之下,遍历
vector<string>
并不会困难,难点在于增删元素,和对迭代器的处理。
- 在我的数据结构之下,遍历
对于难点1:c++ primer学了总得应用一下,边遍历数组还边删元素又不是不可行,在这调了一会,果然成了,最后超时了(通过样例80/81),发现忘记erase和insert是o(n)复杂度的了。这样一层递归就直接o(n2)了,递归还会最坏在外面套一层o(n2)(在完全图的情况下。)
for(auto itAdj=adjs.begin();itAdj!=adjs.end();itAdj++)
{
string adj = *itAdj;
// cout<<adj<<endl;
path.emplace_back(adj);
itAdj = adjs.erase(itAdj); // 迭代器删了会默认指向下一个元素
// cout<<"剩余选择:{";
// for(auto val:adjs) cout<<val<<" "; cout<<"}"<<endl;
dfs(adj);
if(findRet) return;
path.pop_back();
itAdj = adjs.insert(itAdj,adj); // 插入回来之后,迭代器指向插入的元素,说明实际上恢复如初了。
}
所以要选择一个1. 有序 2. 容易增删元素,迭代器不会失效。
unordered_map<string, map<string,int>>;
在遍历的时候,每层循环只需要
for(auto& [k,v]:map) // o(n)
2. 最终代码
class Solution {
public:
vector<string> path{"JFK"};
unordered_map<string, map<string, int>> m;
size_t retSize;
bool findRet = false;
vector<string> findItinerary(vector<vector<string>>& tickets) {
for(auto& vs:tickets)
{
m[vs[0]][vs[1]] += 1;
}
retSize = tickets.size()+1;
dfs("JFK");
return path;
}
bool dfs(string start)
{
if(path.size() == retSize)
{
return true;
}
for(auto& [dst, cnt]:m[start])
{
if(cnt > 0)
{
cnt--;
path.emplace_back(dst);
if(dfs(dst)) return true;
path.pop_back();
cnt++;
}
}
return false;
}
};