题目链接:332. Reconstruct Itinerary
题目大意:给出一个结点对的集合,结点对中为飞机的起飞点与降落点。要求根据飞机机票的信息来重新构造出旅途的线路,即遍历这个图。
注意点:
- 题目给出一定会存在一个解,即构造的图一定是连通的。
- 这是一个有向图,要注意起始与结束地点
- 与一般的深度搜索不同,这次我们不是要搜索结点的访问次序,而是搜索边的访问次序。这就意味着结点可以重复访问,而每条边只能访问一次。
- 若有多种路线可选时候,题目要求输出字典序最小的一条路线,这就要用到集合multiset或者优先队列等数据结构来存储这个图。
一.算法设计
显然这是一道图的深度搜索题目,且要针对图的每一条都搜索一次,找出字典序最小的路线。首先,我们要根据结点对来构建出图,可以用邻接表来表示图。由于方便比较字典序,这里我使用了multiset来储存每一个结点能到达的结点,以此达到建图的目的。
其次,根据深度搜索的思想,我们每遍历到的结点或者边应该设置一个标记表示访问结束,下次不再重复访问。而在本道题目中,可以将已经访问过的边的移出集合,每一次只对集合中仅剩的元素进行遍历。当集合无元素的时候,证明已经到达搜索的最深处,将结点的值保留在vector中用以输出。
由于,根据递归我们得到的序列是从最深处再回溯到起始点,所以在输出的时候要将vector的元素反转输出,从而得到重构的旅途线路。
二.代码实现
class Solution {
public:
vector<string> findItinerary(vector<pair<string, string>> tickets) {
//建立邻接表,构造图
std::map<string, multiset<string>> m;
//存储路径,用于输出
std::vector<string> v;
for(int i = 0; i < tickets.size(); i++){
m[tickets[i].first].insert(tickets[i].second);
}
string start = "JFK";
dfs(start, m, v);
return vector<string>(v.rbegin(),v.rend());
}
void dfs(string start, map<string, multiset<string>>& m, std::vector<string>& v){
while(m[start].size() > 0){
string str = *(m[start].begin()); //拿取头元素
m[start].erase(m[start].begin());
dfs(str,m,v);
}
v.push_back(start);
}
};
三.改进方法
既然我们可以利用multiset来进行排序,也可以用优先队列来处理。leetcode给的展示代码就是用的这一种数据结构,除此之外还利用了auto的c++11属性来简化迭代子。
class Solution {
private:
unordered_map<string, priority_queue<string, vector<string>, greater<string>>> graph;
vector<string> result;
void dfs(string vtex)
{
auto & edges = graph[vtex];
while (!edges.empty())
{
string to_vtex = edges.top();
edges.pop();
dfs(to_vtex);
}
result.push_back(vtex);
}
public:
vector<string> findItinerary(vector<pair<string, string>> tickets) {
for (auto e : tickets)
graph[e.first].push(e.second);
dfs("JFK");
reverse(result.begin(), result.end());
return result;
}
};