题目
给定一个机票的字符串二维数组 [from, to],子数组中的两个成员分别表示飞机出发和降落的机场地点,对该行程进行重新规划排序。所有这些机票都属于一个从 JFK(肯尼迪国际机场)出发的先生,所以该行程必须从 JFK 开始。
说明:
如果存在多种有效的行程,你可以按字符自然排序返回最小的行程组合。例如,行程 ["JFK", "LGA"] 与 ["JFK", "LGB"] 相比就更小,排序更靠前
所有的机场都用三个大写字母表示(机场代码)。
假定所有机票至少存在一种合理的行程。
示例 1:
输入: [["MUC", "LHR"], ["JFK", "MUC"], ["SFO", "SJC"], ["LHR", "SFO"]]
输出: ["JFK", "MUC", "LHR", "SFO", "SJC"]
示例 2:
输入: [["JFK","SFO"],["JFK","ATL"],["SFO","ATL"],["ATL","JFK"],["ATL","SFO"]]
输出: ["JFK","ATL","JFK","SFO","ATL","SFO"]
解释: 另一种有效的行程是 ["JFK","SFO","ATL","JFK","ATL","SFO"]。但是它自然排序更大更靠后。
解题思路
题目意思实际上是求有向图中,遍历所有边的一种路径。看了题解才明白,实际上这是求半欧拉图中的欧拉通路的问题。
一些基本概念:
- 通过图中所有边恰好一次且行遍所有顶点的通路称为欧拉通路。
- 通过图中所有边恰好一次且行遍所有顶点的回路称为欧拉回路。
- 具有欧拉回路的无向图称为欧拉图。
- 具有欧拉通路但不具有欧拉回路的无向图称为半欧拉图。
Hierholzer 算法
这个算法可以用来寻找连通图中的欧拉路径,步骤如下:
- 从起点出发,进行深度优先搜索。
- 每次沿着某条边从某个顶点移动到另外一个顶点的时候,都需要删除这条边。
- 如果没有可移动的路径,则将所在节点加入到栈中,并返回。
因为题目需要求出字典序最小的路径,所以在保存节点的邻居节点时,注意按照字典序逆序排列,然后访问邻居节点时用pop
来访问
因为在取邻居节点时,每次都要取字典序最小的那个,所以也可以考虑用堆来保存邻居节点,这样可以更快一些
代码
栈版:
class Solution:
def findItinerary(self, tickets: List[List[str]]) -> List[str]:
graph = {}
for source, target in tickets:
if source not in graph:
graph[source] = []
graph[source].append(target)
for key in graph:
graph[key].sort(reverse = True)
stack = []
def dfs(spot: str) -> None:
while spot in graph and graph[spot]:
next_spot = graph[spot].pop()
dfs(next_spot)
stack.append(spot)
dfs('JFK')
return stack[::-1]
堆版:
class Solution:
def findItinerary(self, tickets: List[List[str]]) -> List[str]:
import heapq
graph = {}
for source, target in tickets:
if source not in graph:
graph[source] = []
heapq.heappush(graph[source], target)
stack = []
def dfs(spot: str) -> None:
while spot in graph and graph[spot]:
next_spot = heapq.heappop(graph[spot])
dfs(next_spot)
stack.append(spot)
dfs('JFK')
return stack[::-1]