Minimize Malware Spread

HardUnion-Find · Connected Components🧩 Union-FindLeetCode #924 ↗Updated

Problem

A network of n machines is given as a symmetric adjacency matrix graph (graph[i][j] == 1 means a direct link). The machines in initial are infected, and malware spreads along links until nothing new can be reached. You may remove exactly one machine from initial (it stays in the network and can still be reinfected). Return the machine whose removal leaves the fewest infected machines; break ties by the smallest index.
Inputgraph = [[1,1,0,0],[1,1,0,0],[0,0,1,1],[0,0,1,1]], initial = [2,0]Output0
Inputgraph = [[1,1,0],[1,1,1],[0,1,1]], initial = [0,2]Output0
Inputgraph = [[1,0,0],[0,1,1],[0,1,1]], initial = [2,0]Output2

2 ≤ n ≤ 300  ·  graph[i][i] = 1, graph symmetric  ·  1 ≤ initial.length ≤ n  ·  initial values distinct

Try it yourself

Java
class Solution {
    public int minMalwareSpread(int[][] graph, int[] initial) {
        
    }
}
Your code is saved in this browser.Submit on LeetCode ↗
Key insight
Malware fills whole connected components. Removing a node only helps if it is the only initially infected node in its component — then the entire component is saved. So pick the uniquely-infected component with the largest size.
PatternUnion-Find

Instinct Items get grouped by "connected", "same as" or "shares a" links and you keep asking which group something is in: merge sets with Union-Find.

Memory hack Find your boss, merge the bosses; squash the chain (path compression) and hang the short tree under the tall one (rank).

Open the Union-Find guide →

Approaches

Brute ForceSimulate Every Removal

Time O(k·n2)Space O(n)

For each candidate in initial (in ascending order), run a multi-source BFS from the other infected machines on the matrix and count how many end up infected. Keep the candidate with the smallest count; scanning in ascending order with a strict < settles ties. k = |initial|.

Java
public int minMalwareSpread(int[][] graph, int[] initial) {
    int n = graph.length;
    int[] candidates = initial.clone();
    Arrays.sort(candidates);
    int best = candidates[0], bestCount = Integer.MAX_VALUE;
    for (int removed : candidates) {
        boolean[] infected = new boolean[n];
        Deque<Integer> queue = new ArrayDeque<>();
        for (int s : initial) {
            if (s != removed) {
                infected[s] = true;
                queue.add(s);
            }
        }
        int count = queue.size();
        while (!queue.isEmpty()) {
            int u = queue.poll();
            for (int v = 0; v < n; v++) {
                if (graph[u][v] == 1 && !infected[v]) {
                    infected[v] = true;
                    count++;
                    queue.add(v);
                }
            }
        }
        if (count < bestCount) {
            bestCount = count;
            best = removed;
        }
    }
    return best;
}

Faithful to the statement, but up to n BFS runs of O(n2) each.

At a glance

LevelApproachTimeSpace
Brute ForceSimulate Every RemovalO(k·n2)O(n)
OptimalUnion-Find Component SizesO(n2·α(n))O(n)
Common pitfalls
  • Picking the largest component even when it holds 2+ infected nodes — removal saves nothing there.
  • Forgetting the fallback: if no component is uniquely infected, return min(initial).
  • Assuming initial is sorted — tie-breaks must use the actual smallest index.
Interview tips
  • Explain the observation first: infection is “all or nothing” per component.
  • Walk through the tie-break and fallback cases explicitly; they are where bugs hide.
  • Mention the harder variant (LC 928) where the removed node is deleted from the graph.
Pattern
Union-Find with component sizes → questions whose effect spreads across an entire connected component.
My notes saved in this browser