847. 访问所有节点的最短路径
Difficulty
Hard
Tags
BFS
URL
https://leetcode.cn/problems/shortest-path-visiting-all-nodes/
Star
存在一个由
n
个节点组成的无向连通图,图中的节点按从 0
到 n - 1
编号。给你一个数组
graph
表示这个图。其中,graph[i]
是一个列表,由所有与节点 i
直接相连的节点组成。返回能够访问所有节点的最短路径的长度。你可以在任一节点开始和停止,也可以多次重访节点,并且可以重用边。
示例 1:

输入:graph = [[1,2,3],[0],[0],[0]] 输出:4 解释:一种可能的路径为 [1,0,2,0,3]
示例 2:

输入:graph = [[1],[0,2,4],[1,3,4],[2],[1,2]] 输出:4 解释:一种可能的路径为 [0,1,4,2,3]
提示:
n == graph.length
1 <= n <= 12
0 <= graph[i].length < n
graph[i]
不包含i
- 如果
graph[a]
包含b
,那么graph[b]
也包含a
- 输入的图总是连通图
通过次数22,481提交次数33,093
法1 BFS + 状态压缩
关键点:
本题hard在何处?在于对状态的表示
在一般的BFS中,我们一般只记录自己当前在哪个点,以及过去访问过哪些点,且这些点是无重复的。
在本题,以示例为例,在第一次访问0和第二次访问0的时候,其实完全是两种状态。第一次访问0时,已访问过1;第二次访问0时,已访问过0,1,2。

在搜索时,我们希望能带着这个
visited
进行搜索,但是如果带着一个数组或者集合,显然非常不方便,拷贝起来非常慢,一般这种时候我们会考虑回溯。但本题需要找到一个 最短的路径,因此回溯就显得不合时宜了(回溯适合以下两种情况:找到所有方案数量、找到所有方案)。观察到
n
是一个比较小的数,因此可以用状态压缩,即用一个数字来表示状态。即用二进制中的某一位来表示 当前位置是否被访问过,例如 0110表示位置 1 2 被访问过。注意,本题的
visited
存储的是 (point, state)
的集合,而非常规BFS中的 point
的集合。思路
题解
class Solution: def shortestPathLength(self, graph: List[List[int]]) -> int: n = len(graph) target = (1 << n) - 1 # 遍历所有的起点 res = float("inf") for s in range(0, n): res = min(res, self.helper(s, target, graph)) return res def helper(self, start: int, target: int, graph:List[List[int]]): """从 start 出发 需要的长度 """ queue = deque() visited = set() queue.append((start, 1<<start)) # 当前位置,状态 visited.add((start, 1<<start)) step = 0 while queue: step += 1 size = len(queue) # print(queue) while size: cur_loc, cur_state = queue.popleft() size -= 1 for nxt_loc in graph[cur_loc]: nxt_state = cur_state | 1 << nxt_loc if nxt_state == target: return step if (nxt_loc, nxt_state) in visited: continue visited.add((nxt_loc, nxt_state)) queue.append((nxt_loc, nxt_state)) return 0