边工作边刷题:70天一遍leetcode: day 91
Reconstruct Itinerary
要点:注意这题不是让找cycle,只需要从”JFK"开始cover所有站就可以。所以基本方法就是按图做dfs遍历直到找到一条valid的路径
- dfs找路径的要点有二:1是返回boolean来表示是否找到,从而可以直接返回,2是在每一个点分支要不断push=>pop来backtrack
- 因为同一站可能在同一路径上经过多次,所以每层处理完了一个destination就要从当前分支中remove,下层返回后又要加入分支backtrack。这里就需要loop的同时remove/add,所以不能用for each loop,而是要按初始结点个数。这是与topological sort最主要的差别,因为topological sort还要检测cycle,所以不能remove。而这题cycle只是多余的,不要经过就好。
- 和topological sort的区别还有只是从单结点出发,找一条cover全部的路径,而不是全部分支都遍历。所以dfs返回false继续下一条边,而true就结束了。
- 这里的一个难点是因为要保持分支遍历的顺序,所以要用queue(FIFO)来remove/add分支:python中是deque(注意list模拟queue的效率不好)
- 结束条件是res.size()==n。这里假设了ticket都会用光的。
错误点:
- 因为用的是defaultdict,所以要检查下一个结点是否在graph里。注意,某一站可能不在任何ticket的出发点
- 因为图邻接点有限,所以用TreeSet远慢于直接用list
class Solution(object):
def findItinerary(self, tickets):
"""
:type tickets: List[List[str]]
:rtype: List[str]
"""
def dfs(graph, res, n):
if len(res)==n:
return True
source = res[-1]
# print graph, source
if source in graph: # error 1: if no check, problem with defaultdict
for i in xrange(len(graph[source])): # error 3: similar to bfs, loop while remove/get back
# to = graph[source][i] error 3/4: can't loop on index:
to = graph[source].popleft() # error 2: remove it so that not used anymore on the same path
res.append(to)
if dfs(graph, res, n):
return True
res.pop()
graph[source].append(to)
return False
graph = collections.defaultdict(list)
for t in tickets:
graph[t[0]].append(t[1])
for k in graph:
graph[k].sort()
graph[k]=collections.deque(graph[k]) # error 2: should use a deque
# print graph
res = []
res.append("JFK")
dfs(graph, res, len(tickets)+1)
return res