
Problem Statement
Navigate a unique problem in graph theory where we have an undirected tree composed of nodes labeled from 0
to n - 1
, connected by n - 1
edges structured as defined in an array edges
. Each pair within this array represents a direct link between two nodes. Along with these regular nodes, we are also furnished with a set of restricted
nodes, listed in an integer array. The challenge lies in figuring out the maximum number of nodes that can be accessed from the starting node 0
, ensuring none of these accessed nodes are restricted
. It's important to note that the starting node 0
is never included in the restricted
nodes, simplifying the initial condition but making the traversal and decision-making process a strategic endeavor.
Examples
Example 1
Input:
n = 7, edges = [[0,1],[1,2],[3,1],[4,0],[0,5],[5,6]], restricted = [4,5]
Output:
4
Explanation:
The diagram above shows the tree. We have that [0,1,2,3] are the only nodes that can be reached from node 0 without visiting a restricted node.
Example 2
Input:
n = 7, edges = [[0,1],[0,2],[0,5],[0,4],[3,2],[6,5]], restricted = [4,2,1]
Output:
3
Explanation:
The diagram above shows the tree. We have that [0,5,6] are the only nodes that can be reached from node 0 without visiting a restricted node.
Constraints
2 <= n <= 105
edges.length == n - 1
edges[i].length == 2
0 <= ai, bi < n
ai != bi
edges
represents a valid tree.1 <= restricted.length < n
1 <= restricted[i] < n
- All the values of
restricted
are unique.
Approach and Intuition
By examining the problem and examples provided, one deduces the necessity to adopt a graph traversal technique that is best suited for trees. Here’s how we might conceptualize the navigation to yield the desired maximum count of accessible nodes:
Model the Graph:
- Convert the list of edges into a more operable graph representation, specifically an adjacency list, which simplifies the access to neighboring nodes.
Manage Restrictions:
- Store the
restricted
nodes in a set for O(1) time complexity check, ensuring efficient validation during traversal if a node should be avoided.
- Store the
Graph Traversal:
- Deploy a Breadth-First Search (BFS) from node
0
, as BFS aptly handles unweighted connections characteristic of trees for uniform level exploration. - Start with node
0
in a queue and a set to track visited nodes due to the cyclic-free nature of trees which guarantees a simple check.
- Deploy a Breadth-First Search (BFS) from node
Node Processing:
- For each node being processed, check each of its neighbors. If a neighbor has not been visited nor is it in the
restricted
set, it qualifies to be added to the queue for further traversal and also can be safely marked as visited.
- For each node being processed, check each of its neighbors. If a neighbor has not been visited nor is it in the
Increment and Track:
- For each valid processing of node, increment a counted variable, which eventually turns out to be the maximum number of nodes starting from
0
that are not restricted.
- For each valid processing of node, increment a counted variable, which eventually turns out to be the maximum number of nodes starting from
The approach rests on the assumption that all input parameters are within the described constraints, and each node only connects with others in a manner that maintains the tree's integrity, i.e., no cycles and exactly n - 1
edges for n
nodes, firmly situating the problem within the realm of trees rather than generalized graphs. This set-up guides strategic traversal and application-specific optimizations.
Solutions
- C++
- Java
- Python
class Solution {
public:
void explore(int node, int& count, vector<vector<int>>& adjList, vector<bool>& visited) {
// Increment count for each node visited
count++;
visited[node] = true;
// Explore adjacent nodes that are not visited
for (auto neighbor : adjList[node]) {
if (!visited[neighbor]) {
explore(neighbor, count, adjList, visited);
}
}
}
int findReachable(int nodes, vector<vector<int>>& edgesList, vector<int>& blockList) {
// Initialize adjacency list for nodes
vector<vector<int>> adjList(nodes);
for (auto edge : edgesList) {
int first = edge[0], second = edge[1];
adjList[first].push_back(second);
adjList[second].push_back(first);
}
// Set restricted nodes as visited
vector<bool> visited(nodes, false);
for (auto blocked : blockList) {
visited[blocked] = true;
}
int result = 0;
explore(0, result, adjList, visited);
return result;
}
};
This C++ solution tackles the problem of determining the number of nodes reachable in an undirected graph, with certain nodes being restricted. The implementation utilizes a depth-first search (DFS) technique, incorporating an adjacency list for graph representation and a boolean vector for tracking visited nodes, including restricted ones.
Solution Process
Adjacency List Construction:
- An adjacency list is formed from the given list of edges. For each pair of connected nodes in the
edgesList
, the connection is established bidirectionally.
- An adjacency list is formed from the given list of edges. For each pair of connected nodes in the
Setting Up Restrictions:
- A vector named
visited
initializes with all nodes set tofalse
. Nodes that are in theblockList
are then marked astrue
invisited
to denote that these nodes are not to be visited during the DFS traversal.
- A vector named
DFS Implementation:
- The DFS starts from node 0.
- A helper function
explore
is used to traverse the graph. It marks the current node as visited and explores each adjacent node that has not been visited yet, avoiding the restricted nodes. This function keeps a running count of all nodes that have been visited.
Count of Reachable Nodes:
- After the exploration, the count of reachable nodes is returned. The count reflects the number of nodes that can be reached from node 0, excluding the nodes that are restricted.
Key Features and Considerations
Handling of Connected Components:
- The solution assumes that the graph is connected or that node 0 has paths to all other non-restricted nodes. For graphs that aren’t fully connected, modifications are necessary to handle other components.
Efficiency:
- The DFS approach ensures that each node and edge is visited once in the best-case scenario, barring the restricted nodes, resulting in a time complexity approximately of O(V + E), where V is the number of vertices and E is the number of edges.
Initialization of Data Structures:
- Both the adjacency list and the
visited
vector are crucial for efficient access and traversal. Adjustments to the graph input or blocked nodes input can be managed by simply updating these structures.
- Both the adjacency list and the
This approach primarily uses standard graph traversal techniques and data structures for graph representation, ensuring clarity, efficiency, and adaptability for diverse graph-related problems.
class GraphTraversal {
private int counter = 0;
private void search(int node, Map<Integer, List<Integer>> adjList, Set<Integer> visited) {
counter++;
visited.add(node);
for (int neighbor : adjList.get(node)) {
if (!visited.contains(neighbor)) {
search(neighbor, adjList, visited);
}
}
}
public int countReachableNodes(int count, int[][] connections, int[] noAccess) {
Map<Integer, List<Integer>> adjList = new HashMap<>();
for (int[] connection : connections) {
int x = connection[0], y = connection[1];
adjList.computeIfAbsent(x, k -> new ArrayList<>()).add(y);
adjList.computeIfAbsent(y, k -> new ArrayList<>()).add(x);
}
Set<Integer> visited = new HashSet<>();
for (int restrict : noAccess) {
visited.add(restrict);
}
search(0, adjList, visited);
return counter;
}
}
This Java solution pertains to the problem of counting reachable nodes within a graph, given certain nodes that are not accessible. The solution is implemented with a class GraphTraversal
containing key methods to handle the graph traversal and reachability analysis.
The approach involves:
- Building an adjacency list
adjList
from the input arrayconnections
. This list maps each node to its neighbors facilitating undirected graph traversal. - Handling restricted nodes by adding them to a
visited
HashSet, which ensures these nodes are not accessible during traversal. - The primary traversal method
search
employs Depth First Search (DFS) to explore nodes, counting each accessible node starting from a specific node (node 0 in this case). - The
search
method is recursively called for unvisited neighbors found in the adjacency list, thereby recognizing all connected nodes that aren't restricted. - The
countReachableNodes
method orchestrates the initialization and execution of the search process and returns the total count of reachable nodes.
The solution uses two primary data structures:
- A HashMap for an adjacency list to manage the mapping of each node to its connected nodes efficiently.
- A HashSet to handle visited and restricted nodes ensuring nodes are visited once and restricted nodes are avoided.
This approach ensures effective traversal through recursive logic and returns an accurate count of the nodes reachable from the starting node, considering any restrictions.
class Solution:
def totalReachableNodes(self, nodes: int, connections: List[List[int]], banned: List[int]) -> int:
# Creating a map to track neighbors of each node from edges
edges_map = collections.defaultdict(list)
for vertex1, vertex2 in connections:
edges_map[vertex1].append(vertex2)
edges_map[vertex2].append(vertex1)
# Using a list to track visited nodes, setting restricted nodes as already visited
visited = [False] * nodes
for res_node in banned:
visited[res_node] = True
def traverse(node):
# Increment total reachable count and mark current node as visited
self.reachable_count += 1
visited[node] = True
# Recursively visit all connected nodes that haven't been visited
for neighbor in edges_map[node]:
if not visited[neighbor]:
traverse(neighbor)
self.reachable_count = 0
traverse(0) # Start traversal from node 0 (assumed root node)
return self.reachable_count
The Python solution provided calculates the total number of nodes that can be reached in an undirected graph, given some nodes are banned from being visited. The graph is represented by its connection pairs and a list of banned nodes. The solution employs a depth-first search (DFS) traversal to explore reachable nodes, initiated from node 0 (assuming it's not banned).
- Firstly, the solution constructs an adjacency list (
edges_map
) from the list of edges (connections
) which depicts direct connections between nodes. - It initializes a list (
visited
) that keeps track of which nodes have been visited or are banned. Nodes listed inbanned
are set as visited from the start. - The main function
traverse
is defined to perform DFS:- It increments a counter (
reachable_count
) each time a node is visited. - It then marks the currently visited node and recursively visits its neighboring nodes if they have not been visited yet.
- It increments a counter (
- The traversal starts at node 0, and after traversing, the total count of reachable nodes is returned.
Here are the steps the function goes through:
- Initialize the adjacency list for node connections.
- Mark all banned nodes as visited to prevent visiting them.
- Define and use a recursive DFS function to count all reachable nodes starting from the desired node, typically node 0.
- Execute the DFS and calculate the total number of reachable nodes excluding the banned ones.
The function handles the scenario where certain nodes are restricted, effectively counting only those nodes that can be reached through allowed paths.
No comments yet.