AlgoMaster Logo

Shortest Path Visiting All Nodes

hardFrequency9 min readUpdated June 23, 2026

Understanding the Problem

We need to find the shortest path (fewest edges) that visits every node in an undirected graph. There are a few things that make this different from typical shortest path problems.

First, we can start at any node. There's no fixed source. Second, we can revisit nodes and reuse edges. This is critical because the graph might not have a Hamiltonian path (a path that visits every node exactly once), so backtracking through already-visited nodes might be necessary. Third, "shortest" means fewest edges traversed, not fewest unique nodes.

The challenge is tracking which nodes we've visited along the way. A plain BFS over nodes does not work, because the state isn't only the current position. Two paths that arrive at the same node but have visited different subsets of nodes are different states with different remaining work. The state therefore has to combine where we are with what we've already visited. That definition drives every approach to this problem.

Key Constraints:

  • 1 <= n <= 12: with at most 12 nodes, the visited set has at most 2^12 = 4096 subsets, which makes exponential-in-n approaches over subsets feasible. This is the constraint that points toward bitmask techniques.
  • The graph is connected: every node is reachable from every other node, so a path visiting all nodes always exists.
  • We can start at any node: the search has to consider all n starting positions, not a single fixed source.

Approach 1: Brute Force (Backtracking)

Intuition

Try every order in which the nodes could be visited and keep the shortest walk that covers all of them. Start from each node in turn and use backtracking to explore the next moves: at each step move to a neighbor, either an unvisited one or a visited one used as a stepping stone toward an unvisited node. When every node has been visited, record the number of edges taken.

This enumerates walks directly, so it is expensive, but it states exactly what we are minimizing: the number of edges in a walk that touches all n nodes.

Because a walk may pass through already-visited nodes, the search needs a bound, otherwise it can cycle between two adjacent nodes forever. A spanning-tree DFS of a connected graph visits every node using at most 2(n-1) edges, so the answer is never larger than that. Seeding the best-so-far with 2(n-1) gives a valid upper bound and lets the edges >= best prune cut off any branch that has already used that many edges without finishing.

Algorithm

  1. Initialize the best answer to 2 * (n - 1), a valid upper bound on the walk length.
  2. For each starting node s from 0 to n-1, run a DFS/backtracking search with s marked visited.
  3. At each step, for every neighbor: if it is unvisited, mark it visited, recurse, then unmark it. If it is already visited, recurse through it without changing the visited set (it serves as a stepping stone toward an unvisited node).
  4. Prune any branch whose edge count has reached the current best.
  5. When all nodes are visited, update the best answer with the current edge count.
  6. Return the best answer found across all starting nodes.

Example Walkthrough

Input:

0
1
2
0
1
2
3
1
0
2
0
3
0
graph

The graph is a star: node 0 is the center, connected to 1, 2, and 3, which are leaves.

We try each starting node. Starting at node 0 (visitedCount=1), we can reach 1, 2, 3 in turn but must return to 0 between leaves, so the walk 0->1->0->2->0->3 takes 6 edges. Starting at a leaf is better. Starting at node 1: 1->0 (visit 0, count 2), 0->2 (visit 2, count 3), 2->0 (move through a visited node, count still 3, edges now 3), 0->3 (visit 3, count 4 = n). That walk is 1,0,2,0,3 with 4 edges. The edges >= minPath prune then cuts off any branch that has already used 4 or more edges without finishing, which is what keeps the search from looping through visited nodes forever. The minimum over all starting nodes is 4.

4
result

Code

Backtracking re-derives the same (current node, visited set) combination over and over, because it has no memory of states it already reached by a shorter walk. The next approach gives each distinct state a single best distance and visits it once.

Approach 2: BFS on State Space (Bitmask BFS)

Redefine the state. In ordinary BFS on a graph, the state is the current node. Here, a path that has visited {0, 1, 2} and one that has visited {0, 1, 3} face different remaining work even if both currently sit on the same node, so both pieces of information belong in the state.

The state becomes a pair: (current node, set of visited nodes). With n ≤ 12, the visited set fits in a bitmask where bit i is set when node i has been visited. That gives at most n 2^n states, which for n=12 is 12 4096 = 49,152.

Run BFS over this state space. Because BFS expands states in order of increasing distance, the first time it reaches a state whose mask has all bits set, that distance is the shortest path length.

Since we can start at any node, we seed the BFS queue with all n starting states: (node i, bitmask with only bit i set) for each node i. The target is any state where the bitmask equals (1 << n) - 1, meaning all n bits are on.

Algorithm

  1. Let n be the number of nodes and fullMask = (1 << n) - 1 (all n bits set).
  2. Create a queue and a visited set for the state space.
  3. For each node i from 0 to n-1, enqueue the state (i, 1 << i) with distance 0. Mark it visited.
  4. Run BFS: dequeue a state (node, mask).
  5. If mask equals fullMask, return the current distance.
  6. For each neighbor of node, compute newMask = mask | (1 << neighbor).
  7. If state (neighbor, newMask) hasn't been visited, enqueue it with distance + 1 and mark visited.
  8. The BFS guarantees we find the shortest path first.

Example Walkthrough

1Seed BFS: start from all nodes. States: (0,0001), (1,0010), (2,0100), (3,1000)
0mask=00011mask=00102mask=01003mask=1000
1/5
1Seed all starting states at distance 0
Front
(0,0001)
(1,0010)
(2,0100)
(3,1000)
Rear
1/5

Code

The BFS over states is efficient on its own. A dynamic programming formulation solves the same problem from a different angle, separating the shortest distance between nodes from the order in which to visit them.

Approach 3: Bitmask DP

Intuition

The state is again (current node, visited mask), but instead of expanding distance by distance, this approach builds up answers from smaller visited sets to larger ones.

Define dp[mask][i] as the minimum number of edges in a walk that has visited exactly the nodes in mask and currently sits on node i. The answer is the minimum of dp[fullMask][i] across all nodes i.

The base case: dp[1 << i][i] = 0 for every node i, since starting at node i with only node i marked costs 0 edges.

The transition relies on a precomputed all-pairs shortest distance dist[i][j]. From state (mask, i), moving on to a node j not yet in mask costs dp[mask][i] + dist[i][j], and lands in state (mask | (1 << j), j). The value dist[i][j] already includes any edges spent passing through other nodes to get from i to j, which is why the DP can treat "go to the next new node" as a single weighted step.

The decomposition splits the work in two: the all-pairs BFS answers "what is the shortest way from A to B?", and the bitmask DP answers "in what order should the nodes be visited?".

Algorithm

  1. Precompute shortest distances between all pairs using BFS from each node.
  2. Initialize dp[1 << i][i] = 0 for each node i.
  3. Iterate through all masks from smallest to largest (by value).
  4. For each mask and each node i where bit i is set in mask:
    • For each node j not in mask, update dp[mask | (1 << j)][j] = min(dp[mask | (1 << j)][j], dp[mask][i] + dist[i][j]).
  5. Return the minimum of dp[fullMask][i] for all i.

Example Walkthrough

graph
1Initialize: dp[00001][0]=0, dp[00010][1]=0, ..., dp[10000][4]=0
0cost=01cost=02cost=04cost=03cost=0
dp[fullMask] (final costs)
1Base case: each node starts with cost 0 in its own singleton mask
dp[00001][0]
:
0
dp[00010][1]
:
0
dp[00100][2]
:
0
dp[01000][3]
:
0
dp[10000][4]
:
0
1/5

Code