题目描述
存在一个由 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
输入的图总是连通图
链接:https://leetcode-cn.com/problems/shortest-path-visiting-all-nodes
首先,题目要求最短路径,所以,我们可以考虑使用BFS来做,但是,这里有个问题。
在传统的BFS中,我们需要一个visited记录被访问过的节点,防止BFS回头访问。
但是,本题我们不能这么做,请看下图,考虑 0 这个节点,从 1->0 访问一次,从 1->0->2->0 访问第二次,这是合法的,而且我们也必须这么来做。
所以,我们需要记录整个走过的路径做为visited的key来记录某个节点在这条路径下是否访问过。
简单点,我们可以直接把路径转换成字符串来作为key,比如,"1->0->2->0"作为0这个节点第二次被访问的key。
但是,如果出现 "1->0->2->0->2->0" 怎么办呢?可以看到进入死循环了,其实这种就算重复访问了,它跟"1->0->2->0"应该看作重复访问。
所以,我们可以考虑使用数组记录 0,1,2 在当前路径下是否访问过,然而,题目已经明确说了 n 不超过 12,所以我们完全可以使用一个 int 类型来存在路径。
比如,我们声明一个 visited[n][1<<n]
数组,第一维表示当前节点是否被访问过,第二维表示路径的状态,然后使用位运算来更新这个状态即可。
好了,请看代码,加了详细注释:
class Solution {
public int shortestPathLength(int[][] graph) {
int n = graph.length;
// 多节点同时开始BFS
boolean[][] visited = new boolean[n][1 << n];
Queue<int[]> queue = new LinkedList<>();
for (int i = 0; i < n; i++) {
// 当前节点,状态
queue.offer(new int[]{i, 1 << i});
visited[i][1 << i] = true;
}
// 记录BFS的层数,当访问满了所有节点,返回这个层数即可
int level = 0;
while (!queue.isEmpty()) {
level++;
// 一层一层遍历
int size = queue.size();
for (int i = 0; i < size; i++) {
// 当前节点
int[] element = queue.poll();
int curr = element[0];
int currState = element[1];
// 下一层的节点
for (int next : graph[curr]) {
// 注意这个位运算,计算路径状态
int nextState = currState | (1 << next);
// 提前判断访问了下一个节点满足所有节点都访问了,直接返回即可
if (nextState == ((1 << n) - 1)) {
return level;
}
// 下一个节点下一个状态未访问过,下探
if (!visited[next][nextState]) {
queue.offer(new int[]{next, nextState});
visited[next][nextState] = true;
}
}
}
}
return 0;
}
}
时间复杂度计算较复杂,不做要求,运行结果如下:
图片.png