Count Valid Paths in a Tree
HARDDescription
There is an undirected tree with n nodes labeled from 1 to n. You are given the integer n and a 2D integer array edges of length n - 1, where edges[i] = [ui, vi] indicates that there is an edge between nodes ui and vi in the tree.
Return the number of valid paths in the tree.
A path (a, b) is valid if there exists exactly one prime number among the node labels in the path from a to b.
Note that:
- The path
(a, b)is a sequence of distinct nodes starting with nodeaand ending with nodebsuch that every two adjacent nodes in the sequence share an edge in the tree. - Path
(a, b)and path(b, a)are considered the same and counted only once.
Example 1:
Input: n = 5, edges = [[1,2],[1,3],[2,4],[2,5]] Output: 4 Explanation: The pairs with exactly one prime number on the path between them are: - (1, 2) since the path from 1 to 2 contains prime number 2. - (1, 3) since the path from 1 to 3 contains prime number 3. - (1, 4) since the path from 1 to 4 contains prime number 2. - (2, 4) since the path from 2 to 4 contains prime number 2. It can be shown that there are only 4 valid paths.
Example 2:
Input: n = 6, edges = [[1,2],[1,3],[2,4],[3,5],[3,6]] Output: 6 Explanation: The pairs with exactly one prime number on the path between them are: - (1, 2) since the path from 1 to 2 contains prime number 2. - (1, 3) since the path from 1 to 3 contains prime number 3. - (1, 4) since the path from 1 to 4 contains prime number 2. - (1, 6) since the path from 1 to 6 contains prime number 3. - (2, 4) since the path from 2 to 4 contains prime number 2. - (3, 6) since the path from 3 to 6 contains prime number 3. It can be shown that there are only 6 valid paths.
Constraints:
1 <= n <= 105edges.length == n - 1edges[i].length == 21 <= ui, vi <= n- The input is generated such that
edgesrepresent a valid tree.
Approaches
Checkout 2 different approaches to solve Count Valid Paths in a Tree. Click on different approaches to view the approach and algorithm in detail.
Brute-Force Traversal from Each Node
This approach iterates through every possible starting node in the tree. For each starting node, it performs a traversal (like Depth-First Search or Breadth-First Search) to find all possible paths originating from it. During the traversal, it keeps track of the number of prime nodes encountered on the current path. If a path to a destination node contains exactly one prime number, it's counted as a valid path.
Algorithm
- Create a boolean array
isPrimeof sizen+1and populate it using the Sieve of Eratosthenes. - Build an adjacency list for the tree from the
edgesarray. - Initialize a global counter
validPathsto 0. - Iterate through each node
ifrom 1 ton. - For each
i, start a DFS traversaldfs(u, parent, primeCount)fromu=i,parent=-1,primeCount=0. - In the
dfs(u, p, pc)function: a. Update the prime count:new_pc = pc + (isPrime[u] ? 1 : 0). b. Ifnew_pc > 1, stop exploring this path and return. c. Ifnew_pc == 1, it means the path from the start nodeitouis valid. IncrementvalidPaths. d. For each neighborvofu(wherev != p), recursively calldfs(v, u, new_pc). - After the outer loop finishes, return
validPaths / 2to correct for double counting.
First, we need a way to quickly check if a number is prime. We can pre-compute primality for all numbers from 1 to n using the Sieve of Eratosthenes and store the results in a boolean array.
Next, we build an adjacency list representation of the tree from the given edges.
The main part of the algorithm involves a nested loop. The outer loop iterates through each node i from 1 to n, considering it as a potential starting point of a path.
From each starting node i, we initiate a Depth-First Search (DFS). The DFS function, say dfs(u, p, primeCount), will explore the tree, where u is the current node, p is its parent (to avoid going backward), and primeCount is the count of prime numbers on the path from the starting node i to u.
Inside the DFS, we first update primeCount based on whether the current node u is prime.
If the primeCount for the path from i to u is exactly 1, we've found a valid path (i, u). We've found a valid path (i, u). We increment our total count.
The DFS continues recursively to all neighbors of u (except its parent), but only if the primeCount does not exceed 1. If it's already 2 or more, any further extension of the path will also have at least 2 primes, so we can prune this search branch.
Since this process counts each path (a, b) twice (once when starting from a and once from b), the final result is the total count divided by 2.
class Solution {
private List<List<Integer>> adj;
private boolean[] isPrime;
private long count = 0;
private int n;
public long countPaths(int n, int[][] edges) {
this.n = n;
sieve(n);
adj = new ArrayList<>();
for (int i = 0; i <= n; i++) {
adj.add(new ArrayList<>());
}
for (int[] edge : edges) {
adj.get(edge[0]).add(edge[1]);
adj.get(edge[1]).add(edge[0]);
}
for (int i = 1; i <= n; i++) {
// We only need to start from a prime or a node adjacent to a prime
// to find all paths, but for simplicity of brute force, we start from all.
dfs(i, -1, 0);
}
return count / 2;
}
private void dfs(int u, int p, int primeCount) {
if (isPrime[u]) {
primeCount++;
}
if (primeCount > 1) {
return;
}
// If start node is not u, and path is valid, count it.
if (primeCount == 1) {
count++;
}
for (int v : adj.get(u)) {
if (v != p) {
dfs(v, u, primeCount);
}
}
}
private void sieve(int n) {
isPrime = new boolean[n + 1];
if (n >= 2) {
java.util.Arrays.fill(isPrime, true);
}
isPrime[0] = isPrime[1] = false;
for (int i = 2; i * i <= n; i++) {
if (isPrime[i]) {
for (int j = i * i; j <= n; j += i) {
isPrime[j] = false;
}
}
}
}
}
Complexity Analysis
Pros and Cons
- Relatively simple to understand and implement compared to more optimized solutions.
- Correctly solves the problem for small
n.
- Highly inefficient due to redundant computations. The traversal from each node re-explores large parts of the tree.
- Will result in a "Time Limit Exceeded" error for the given constraints (
nup to 10^5).
Code Solutions
Checking out 3 solutions in different languages for Count Valid Paths in a Tree. Click on different languages to view the code.
Video Solution
Watch the video walkthrough for Count Valid Paths in a Tree
Similar Questions
5 related questions you might find useful
Algorithms:
Patterns:
Data Structures:
Companies:
Subscribe to Scale Engineer newsletter
Learn about System Design, Software Engineering, and interview experiences every week.
No spam, unsubscribe at any time.