You are given a network of n
nodes represented as an n x n
adjacency matrix graph
, where the ith
node is directly connected to the jth
node if graph[i][j] == 1
.
Some nodes initial
are initially infected by malware. Whenever two nodes are directly connected, and at least one of those two nodes is infected by malware, both nodes will be infected by malware. This spread of malware will continue until no more nodes can be infected in this manner.
Suppose M(initial)
is the final number of nodes infected with malware in the entire network after the spread of malware stops.
We will remove exactly one node from initial
, completely removing it and any connections from this node to any other node.
Return the node that, if removed, would minimize M(initial)
. If multiple nodes could be removed to minimize M(initial)
, return such a node with the smallest index.
Example 1:
Input: graph = [[1,1,0],[1,1,0],[0,0,1]], initial = [0,1] Output: 0
Example 2:
Input: graph = [[1,1,0],[1,1,1],[0,1,1]], initial = [0,1] Output: 1
Example 3:
Input: graph = [[1,1,0,0],[1,1,1,0],[0,1,1,1],[0,0,1,1]], initial = [0,1] Output: 1
Constraints:
n == graph.length
n == graph[i].length
2 <= n <= 300
graph[i][j]
is 0
or 1
.graph[i][j] == graph[j][i]
graph[i][i] == 1
1 <= initial.length < n
0 <= initial[i] <= n - 1
initial
are unique.When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force method for minimizing malware spread involves trying to remove each initially infected computer, one at a time, and then observing the resulting spread of malware. We want to find which removal leads to the least malware spread in the network.
Here's how the algorithm would work step-by-step:
def minimize_malware_spread_two(graph, initial):
min_infected = float('inf')
best_removal = -1
for node_to_remove_index in range(len(initial)):
node_to_remove = initial[node_to_remove_index]
# Simulate removal by creating new initial list
current_initial = initial[:node_to_remove_index] + initial[node_to_remove_index+1:]
infected = set(current_initial)
stack = list(current_initial)
# Perform breadth-first search to simulate spread
while stack:
current_node = stack.pop(0)
for neighbor, is_connected in enumerate(graph[current_node]):
if is_connected == 1 and neighbor not in infected:
infected.add(neighbor)
stack.append(neighbor)
# Update minimum infected count and node to remove
if len(infected) < min_infected:
min_infected = len(infected)
best_removal = node_to_remove
elif len(infected) == min_infected and node_to_remove < best_removal:
best_removal = node_to_remove
# Returning best removal
if best_removal == -1:
best_removal = min(initial)
return best_removal
The goal is to find the single infected computer that, if removed, would stop the spread of malware the most. The best way to do this involves figuring out, for each infected computer, which other computers it exclusively affects.
Here's how the algorithm would work step-by-step:
def minMalwareSpread(graph, initial):
number_of_nodes = len(graph)
infected = set(initial)
def depthFirstSearch(node, visited, component):
visited[node] = True
component.add(node)
for neighbor in range(number_of_nodes):
if graph[node][neighbor] == 1 and not visited[neighbor]:
depthFirstSearch(neighbor, visited, component)
components = []
visited = [False] * number_of_nodes
# Identify all connected components.
for node in range(number_of_nodes):
if not visited[node]:
component = set()
depthFirstSearch(node, visited, component)
components.append(component)
node_to_component = {}
for i, component in enumerate(components):
for node in component:
node_to_component[node] = i
component_to_infected_nodes = {}
for i in range(len(components)):
component_to_infected_nodes[i] = []
for infected_node in infected:
component_index = node_to_component[infected_node]
component_to_infected_nodes[component_index].append(infected_node)
saved_nodes = [0] * number_of_nodes
# Count computers exclusively dependent on each infected computer.
for component_index, infected_nodes_in_component in component_to_infected_nodes.items():
if len(infected_nodes_in_component) == 1:
infected_node = infected_nodes_in_component[0]
saved_nodes[infected_node] = len(components[component_index])
max_saved = 0
result = min(initial)
# Choose the infected computer to remove to maximize saved computers.
for infected_node in initial:
if saved_nodes[infected_node] > max_saved:
max_saved = saved_nodes[infected_node]
result = infected_node
elif saved_nodes[infected_node] == max_saved and infected_node < result:
result = infected_node
return result
Case | How to Handle |
---|---|
Empty graph (no connections) | If the graph is empty, removing any initial node won't prevent any spread, so return -1 if all nodes are initially infected. |
No initially infected nodes | If no nodes are initially infected, removing any node will not impact the spread; return 0 if no nodes are initially infected. |
All nodes are initially infected | If all nodes are initially infected, removing any single node will not change the final infection state; return -1. |
Graph contains disconnected components, and some components have initially infected nodes while others don't | Process each connected component separately and analyze the impact of removing a node within the initially infected components. |
Single infected node connected to multiple uninfected nodes | Removing this infected node prevents all connected uninfected nodes from being infected, yielding a good result. |
Large graph with many nodes and edges (scalability) | Employ Union-Find or Depth-First Search (DFS) efficiently to handle large graphs without exceeding time limits by optimizing the component finding process. |
Complete graph (every node connected to every other node) | Removing a single infected node might only marginally decrease the malware spread if other infected nodes exist, so correctly account for overlapping infected areas. |
Graph with cycles | DFS or BFS will correctly traverse cycles, and Union-Find naturally handles cycles during component merging, without stack overflow risks. |