Sale: Get 60% Off on all Pro Plans. Buy Now

Graph Valid Tree

Medium DFS

Problem

Given an integer n and a list of edges representing an undirected graph, determine if the graph is a valid tree, meaning it is connected and contains no cycles.

  • 1 ≤ n ≤ 10⁴
  • 0 ≤ edges.length ≤ 10⁴
  • edges[i].length == 2
  • 0 ≤ edges[i][j] < n
  • No duplicate edges
  • No self-loops

Example

Input: n = 5, edges = [[0,1],[0,2],[0,3],[1,4]]
Output: true

The graph has 5 nodes and 4 edges, which is n-1 edges, a necessary condition for a tree. Starting DFS from node 0, all nodes are visited without encountering a cycle. The graph is connected and acyclic, so it is a valid tree.

Approach

Straightforward Solution

A brute-force approach might check all possible subsets of edges to find a spanning tree, which is exponential and infeasible. Alternatively, one might attempt to detect cycles without verifying connectivity, which can lead to false positives.

Core Observation

A valid tree with n nodes must have exactly n-1 edges and be fully connected without cycles. The absence of cycles can be detected by DFS with a parent tracking mechanism, and connectivity is verified by ensuring all nodes are visited.

Path to Optimal

Preview

The key recognition signals are 'valid tree', 'connected', and 'no cycles'. These indicate a graph traversal approach with cycle detection…

Full step-by-step walkthrough on Pro

Optimal Approach

Preview

Build an adjacency list to represent the graph. Perform a DFS starting from node 0, passing the previous node to avoid revisiting the immediate parent…

Full step-by-step walkthrough on Pro

Want the full reasoning chain?

Unlock the complete walkthrough, line-by-line analysis, and recall drill.

Unlock Pro

Time

O(n + e)

Building the adjacency list takes O(e). DFS visits each node and edge once, resulting in O(n + e) total time.

Space

O(n + e)

The adjacency list stores all edges, requiring O(n + e) space. The visited set stores up to n nodes. The recursion stack can go up to O(n) in the worst case.

Pattern Spotlight

DFS (Cycle Detection and Connectivity Check)

When verifying if an undirected graph is a tree, use DFS to detect cycles by tracking the parent node to avoid false positives from bidirectional edges, and confirm connectivity by ensuring all nodes are visited.

Solution

Python
1class Solution:
2 def validTree(self, n: int, edges: list[list[int]]) -> bool:
3 if not n:
4 return True
5
6 adj = {i: [] for i in range(n)}
7 for n1, n2 in edges:
8 adj[n1].append(n2)
9 adj[n2].append(n1)
10
11 visit = set()
12 def dfs(i, prev):
13 if i in visit:
14 return False
15
16 visit.add(i)
17 for j in adj[i]:
18 if j == prev:
19 continue
20 if not dfs(j, i):
21 return False
22 return True
23
24 return dfs(0, -1) and n == len(visit)

Step-by-Step Solution

1

Construct Adjacency List to Represent the Graph

6adj = {i: [] for i in range(n)}
7for n1, n2 in edges:
8 adj[n1].append(n2)
9 adj[n2].append(n1)

Objective

To build a graph representation that enables efficient traversal and neighbor lookup.

Key Insight

An adjacency list is the most space- and time-efficient way to represent sparse graphs. It allows quick access to neighbors of any node, which is essential for DFS traversal. Initializing the adjacency list with empty lists for all nodes ensures all nodes are accounted for, even isolated ones.

Interview Quick-Check

Core Logic

The adjacency list maps each node to a list of its neighbors, enabling O(1) average-time neighbor access during DFS.

Common Pitfalls & Bugs

Forgetting to add edges in both directions for an undirected graph leads to incorrect traversal and missed connections.

2

Detect Cycles and Track Visited Nodes Using DFS with Parent Tracking

To recursively explore the graph, detect cycles, and mark nodes as visited to verify connectivity.

3

Verify Full Connectivity and Return Final Result

To confirm that all nodes are reachable from the starting node and that no cycles were detected.

4

Handle Edge Case of Zero Nodes

To immediately return true if the graph has no nodes, as an empty graph is trivially a valid tree.

3 more steps with full analysis available on Pro.

Line Analysis

This solution has 8 Critical lines interviewers watch for.

Line 13 Critical
if i in visit:

Check if current node has already been visited.

Encountering a visited node during DFS (other than the parent) indicates a cycle, so returning false here detects cycles early.

Line 18 Critical
if j == prev:

Skip the neighbor if it is the previous node.

Ignoring the edge back to the parent prevents false cycle detection caused by bidirectional edges in undirected graphs.

Line 24 Critical
return dfs(0, -1) and n == len(visit)

Return true if DFS from node 0 found no cycles and all nodes were visited.

Combining cycle detection with connectivity check ensures the graph is a valid tree, as both conditions are necessary and sufficient.

Full line-by-line criticality + rationale for all 18 lines available on Pro.

Test Your Understanding

Why is it necessary to track the previous node during DFS when detecting cycles in an undirected graph?

See the answer with Pro.

Related Problems

DFS pattern

Don't just read it. Drill it.

Reconstruct Graph Valid Tree from memory until it sticks. AlgoDrill blanks out key lines and makes you fill them back in, step by step.

Unlock the Graph Valid Tree drill

or drill a free problem