题目描述
思路解析动画文字版
记住三件事:① 优先走字典序最小的票 ② 走到死路就回溯入栈 ③ 最后把栈逆序。下面一步步看。
这是航线图:三个机场 JFK / SFO / ATL,箭头是机票(有向)。起点 JFK 已标橙。右侧「路线栈」记录我们正踩着的路径,「已完成」是逆序收集的结果栈。
先看清牌面:JFK 有 2 张票(→ATL / →SFO),ATL 有 2 张(→JFK / →SFO),SFO 有 1 张(→ATL),共 5 张。目标是把这 5 张恰好各用一次,连成一笔画。
从起点 JFK 进栈,开始往外飞。站在 JFK,先看它手上还没用的票,挑目的地字典序最小的那张。
站在 JFK:还没用的票通向 [ATL / SFO](蓝色候选)。按字典序挑最小的「ATL」,准备飞 JFK → ATL(这张票就此用掉)。
飞 JFK → ATL:这条边高亮,机票用掉一张。ATL 进「路线栈」,现在站到 ATL,继续在这里挑最小的票。
站在 ATL:还没用的票通向 [JFK / SFO](蓝色候选)。按字典序挑最小的「JFK」,准备飞 ATL → JFK(这张票就此用掉)。
飞 ATL → JFK:这条边高亮,机票用掉一张。JFK 进「路线栈」,现在站到 JFK,继续在这里挑最小的票。
站在 JFK:还没用的票通向 [SFO](蓝色候选)。按字典序挑最小的「SFO」,准备飞 JFK → SFO(这张票就此用掉)。
飞 JFK → SFO:这条边高亮,机票用掉一张。SFO 进「路线栈」,现在站到 SFO,继续在这里挑最小的票。
站在 SFO:还没用的票通向 [ATL](蓝色候选)。按字典序挑最小的「ATL」,准备飞 SFO → ATL(这张票就此用掉)。
飞 SFO → ATL:这条边高亮,机票用掉一张。ATL 进「路线栈」,现在站到 ATL,继续在这里挑最小的票。
站在 ATL:还没用的票通向 [SFO](蓝色候选)。按字典序挑最小的「SFO」,准备飞 ATL → SFO(这张票就此用掉)。
飞 ATL → SFO:这条边高亮,机票用掉一张。SFO 进「路线栈」,现在站到 SFO,继续在这里挑最小的票。
SFO 已经没有未用的票了——这是死路。把 SFO 弹出路线栈、压进结果栈(结果栈逆序收集),回退到上一个机场看还有没有别的票。
ATL 已经没有未用的票了——这是死路。把 ATL 弹出路线栈、压进结果栈(结果栈逆序收集),回退到上一个机场看还有没有别的票。
SFO 已经没有未用的票了——这是死路。把 SFO 弹出路线栈、压进结果栈(结果栈逆序收集),回退到上一个机场看还有没有别的票。
JFK 已经没有未用的票了——这是死路。把 JFK 弹出路线栈、压进结果栈(结果栈逆序收集),回退到上一个机场看还有没有别的票。
ATL 已经没有未用的票了——这是死路。把 ATL 弹出路线栈、压进结果栈(结果栈逆序收集),回退到上一个机场看还有没有别的票。
JFK 已经没有未用的票了——这是死路。把 JFK 弹出路线栈、压进结果栈(结果栈逆序收集),回退到上一个机场看还有没有别的票。
五张票全部用完。把结果栈 [SFO, ATL, SFO, JFK, ATL, JFK] 逆序,就是最终行程:[JFK → ATL → JFK → SFO → ATL → SFO]。
边界先想清。
两个高频追问。
参考代码
def findItinerary(tickets): g = defaultdict(list) for a, b in sorted(tickets): # 按字典序排好 g[a].append(b) res = [] def dfs(u): while g[u]: # 还有未用的票 v = g[u].pop(0) # 取字典序最小的 dfs(v) res.append(u) # 死路:压进结果栈 dfs("JFK") return res[::-1] # 逆序就是答案复杂度
- 时间:O(E log E),排序/堆维护字典序,E 条机票各处理一次
- 空间:O(E),邻接表存 E 条边 + 递归栈 + 结果栈
易错点
面试追问把动画讲成自己的话
追问和普通 DFS 求路径有什么区别?
追问怎么保证字典序最小?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
连接所有点的最小费用
LeetCode 1584 · 中等 · 沿着 高级图 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题