from collections import deque
class Solution:
def shortestPathLength(self, graph):
n = len(graph)
target = (1 << n) - 1 # All nodes visited mask
queue = deque((i, 1 << i) for i in range(n))
visited = set((i, 1 << i) for i in range(n))
steps = 0
while queue:
for _ in range(len(queue)):
node, mask = queue.popleft()
if mask == target:
return steps
for nei in graph[node]:
next_mask = mask | (1 << nei)
if (nei, next_mask) not in visited:
visited.add((nei, next_mask))
queue.append((nei, next_mask))
steps += 1
return -1
#include <vector>
#include <queue>
#include <unordered_set>
using namespace std;
class Solution {
public:
int shortestPathLength(vector<vector<int>>& graph) {
int n = graph.size();
int target = (1 << n) - 1;
queue<pair<int, int>> q;
vector<vector<bool>> visited(n, vector<bool>(1 << n, false));
for (int i = 0; i < n; ++i) {
q.push({i, 1 << i});
visited[i][1 << i] = true;
}
int steps = 0;
while (!q.empty()) {
int sz = q.size();
for (int i = 0; i < sz; ++i) {
auto [node, mask] = q.front(); q.pop();
if (mask == target) return steps;
for (int nei : graph[node]) {
int next_mask = mask | (1 << nei);
if (!visited[nei][next_mask]) {
visited[nei][next_mask] = true;
q.push({nei, next_mask});
}
}
}
steps++;
}
return -1;
}
};
import java.util.*;
class Solution {
public int shortestPathLength(int[][] graph) {
int n = graph.length;
int target = (1 << n) - 1;
Queue<int[]> queue = new LinkedList<>();
boolean[][] visited = new boolean[n][1 << n];
for (int i = 0; i < n; i++) {
queue.offer(new int[]{i, 1 << i});
visited[i][1 << i] = true;
}
int steps = 0;
while (!queue.isEmpty()) {
int sz = queue.size();
for (int i = 0; i < sz; i++) {
int[] curr = queue.poll();
int node = curr[0], mask = curr[1];
if (mask == target) return steps;
for (int nei : graph[node]) {
int nextMask = mask | (1 << nei);
if (!visited[nei][nextMask]) {
visited[nei][nextMask] = true;
queue.offer(new int[]{nei, nextMask});
}
}
}
steps++;
}
return -1;
}
}
/**
* @param {number[][]} graph
* @return {number}
*/
var shortestPathLength = function(graph) {
const n = graph.length;
const target = (1 << n) - 1;
const queue = [];
const visited = Array.from({length: n}, () => Array(1 << n).fill(false));
for (let i = 0; i < n; i++) {
queue.push([i, 1 << i]);
visited[i][1 << i] = true;
}
let steps = 0;
while (queue.length) {
let sz = queue.length;
for (let i = 0; i < sz; i++) {
let [node, mask] = queue.shift();
if (mask === target) return steps;
for (let nei of graph[node]) {
let nextMask = mask | (1 << nei);
if (!visited[nei][nextMask]) {
visited[nei][nextMask] = true;
queue.push([nei, nextMask]);
}
}
}
steps++;
}
return -1;
};
Given an undirected, connected graph represented as an adjacency list graph
where graph[i]
contains all the nodes connected to node i
, your goal is to find the length of the shortest path that visits every node at least once. You can start and stop at any node, and you may revisit nodes and edges as needed. The key constraints are:
graph
is a list of lists where graph[i]
gives the neighbors of node i
.
At first glance, this problem resembles the Traveling Salesman Problem (TSP), where the goal is to visit every node in a graph. However, the key differences are:
The solution uses BFS with state compression via bitmasking. Here's a step-by-step breakdown:
(current_node, visited_mask)
.visited_mask
is an integer where the i-th bit is 1 if node i has been visited.(node, mask)
.mask
has all bits set (i.e., all nodes visited), return the current number of steps.n * 2^n
(n nodes, 2^n possible masks), which is manageable for n ≤ 12
.
Let's walk through a sample input:
Input: graph = [[1,2,3],[0],[0],[0]]
This means:
Brute-force Approach:
n * 2^n
possible states (n nodes, each with 2^n masks).The "Shortest Path Visiting All Nodes" problem is efficiently solved by combining BFS with bitmasking to represent visited nodes. By treating each pair of (current node, visited mask) as a unique state and using BFS to explore all possible paths, we guarantee finding the shortest path that visits every node. The use of bitmasking keeps the solution efficient and elegant, avoiding redundant work and ensuring that the first time all nodes are visited, we have found the optimal answer.