Facebook Pixel

2092. Find All People With Secret

Problem Description

You have n people numbered from 0 to n - 1. Person 0 initially has a secret that they share with person firstPerson at time 0.

You're given a list of meetings where each meeting is represented as [xi, yi, timei], meaning person xi and person yi meet at time timei.

The key rules for how the secret spreads are:

  1. During a meeting: If one person knows the secret when they meet, they immediately share it with the other person
  2. Simultaneous sharing: A person can attend multiple meetings at the same time
  3. Instant propagation: If someone learns the secret at a particular time, they can share it with others in their meetings at that same time

For example, if persons A, B, and C all have meetings at time 5:

  • If A knows the secret and meets B at time 5, B learns it
  • If B also meets C at time 5, B can immediately share it with C
  • This all happens instantaneously at time 5

The goal is to determine which people know the secret after all meetings have concluded. Return a list of all person IDs who know the secret, in any order.

The solution approach groups meetings by time and uses BFS to propagate the secret among all people meeting at each time point, tracking who knows the secret through a visited array.

Flowchart Walkthrough

First, let's pin down the algorithm using the Flowchart. Here's a step-by-step walkthrough:

Is it a graph?

  • Yes: The problem involves people (nodes) and meetings between them (edges). Each meeting creates a connection between two people, and the secret spreads through these connections. We need to model this as a graph where people are vertices and meetings are edges.

Is it a tree?

  • No: The graph is not a tree because:
    • There can be cycles (person A can meet B, B can meet C, and C can meet A)
    • Multiple disconnected components can exist (groups of people meeting separately)
    • There's no hierarchical parent-child relationship

Is the problem related to directed acyclic graphs?

  • No: While there's a time component, the graph itself isn't directed. When two people meet, the secret can flow bidirectionally between them if one knows it.

Is the problem related to shortest paths?

  • No: We're not looking for the shortest path between nodes. We need to find all nodes (people) that can be reached from the initial secret holders through the meeting connections.

Does the problem involve connectivity?

  • Yes: The core problem is about connectivity - determining which people are connected to those who know the secret through chains of meetings at specific times. We need to find all people who become "connected" to secret holders through meetings.

Does the problem have small constraints?

  • Yes: While not explicitly small, the time-grouped approach makes each subproblem manageable. At each timestamp, we only process meetings occurring at that time, creating smaller connected components to explore.

DFS/backtracking?

  • Yes: DFS is perfect for exploring all connected people from those who know the secret. Starting from people with the secret, we traverse through all their connections (meetings) to spread the secret to everyone reachable.

Conclusion: The flowchart suggests using DFS to solve this connectivity problem. The solution uses DFS (implemented via BFS with a queue in the given code) to explore connected components at each timestamp, spreading the secret from known holders to all reachable people through meetings.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is that the secret spreads like a contagion through meetings, but with a crucial twist: it can only spread among people who meet at the same time. This time constraint is what shapes our approach.

Initially, we might think to process meetings one by one, but this would miss an important aspect: if persons A, B, and C all meet at time 5 (A meets B, B meets C), and A knows the secret, then B learns it from A and can immediately share it with C - all at time 5. This "instant propagation" means we need to consider all meetings at the same timestamp together.

Think of each timestamp as creating a temporary network. At time 5, all people meeting at that time form a graph where edges represent meetings. The secret flows through this entire connected component instantly. If anyone in this component knows the secret at the start of time 5, everyone connected to them will know it by the end of time 5.

This leads us to a natural approach:

  1. Group all meetings by their timestamp
  2. For each timestamp, build a graph of who meets whom
  3. Find everyone who already knows the secret in this group
  4. Use graph traversal (DFS/BFS) to spread the secret to all people connected to secret holders

Why DFS/BFS? Because we need to explore all paths from secret holders to mark everyone reachable. It's essentially a reachability problem within each time-based subgraph.

The temporal ordering is critical - we must process earlier times first because someone who learns the secret at time 3 can share it in their meetings at time 5, but not vice versa. This is why we sort meetings by time before processing.

By breaking the problem into time-sliced subproblems and using graph traversal within each slice, we efficiently track how the secret propagates through the entire network of meetings.

Learn more about Depth-First Search, Breadth-First Search, Union Find, Graph and Sorting patterns.

Solution Approach

The implementation follows the intuition of processing meetings in temporal groups and using BFS to propagate the secret within each time slice.

Initial Setup:

  • Create a boolean array vis of size n to track who knows the secret
  • Mark vis[0] = True (person 0 has the secret initially) and vis[firstPerson] = True (they learn it at time 0)
  • Sort all meetings by timestamp to process them chronologically

Time-Grouped Processing: The core algorithm uses a two-pointer technique to group meetings by time:

i = 0
while i < m:
    j = i
    # Find all meetings at the same timestamp
    while j + 1 < m and meetings[j + 1][2] == meetings[i][2]:
        j += 1

This groups meetings [i, j] that occur at the same time.

Building the Graph for Each Timestamp: For each time group, we:

  1. Create an adjacency list g using a defaultdict
  2. Track all people involved in meetings at this time in set s
  3. Build bidirectional edges for each meeting:
for x, y, _ in meetings[i : j + 1]:
    g[x].append(y)
    g[y].append(x)
    s.update([x, y])

BFS Propagation: Starting from people who already know the secret, we spread it through the connected component:

q = deque([u for u in s if vis[u]])  # Start with secret holders
while q:
    u = q.popleft()
    for v in g[u]:  # Check all neighbors
        if not vis[v]:
            vis[v] = True  # Mark as knowing the secret
            q.append(v)   # Add to queue to spread further

The BFS ensures that if anyone in a connected component knows the secret, everyone in that component will learn it.

Why This Works:

  • By processing times in order, we ensure people who learn the secret earlier can share it in later meetings
  • The graph representation at each timestamp captures all possible paths for secret propagation
  • BFS guarantees we find all reachable people from secret holders
  • The vis array persists across time groups, maintaining the cumulative knowledge state

Time Complexity: O(M log M + M + N) where M is the number of meetings and N is the number of people. The sorting takes O(M log M), and processing all meetings takes O(M).

Space Complexity: O(N + M) for the visited array and the temporary graphs created at each timestamp.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a small example to illustrate the solution approach.

Input:

  • n = 6 (people numbered 0-5)
  • firstPerson = 1 (person 1 learns the secret at time 0)
  • meetings = [[1,2,5], [2,3,8], [1,5,10], [3,4,5]]

Step 1: Initial Setup

  • Create vis = [False, False, False, False, False, False]
  • Mark person 0 and person 1 as knowing the secret: vis = [True, True, False, False, False, False]
  • Sort meetings by time: [[3,4,5], [1,2,5], [2,3,8], [1,5,10]]

Step 2: Process Time 5 (meetings at index 0-1)

  • Meetings: [3,4,5] and [1,2,5]
  • Build graph:
    g = {3: [4], 4: [3], 1: [2], 2: [1]}
    s = {1, 2, 3, 4}
  • Find who knows the secret in this group: person 1
  • BFS from person 1:
    • Visit person 1 β†’ spread to person 2
    • Mark vis[2] = True
    • Persons 3 and 4 form a separate component (no one knows secret there)
  • After time 5: vis = [True, True, True, False, False, False]

Step 3: Process Time 8 (meeting at index 2)

  • Meeting: [2,3,8]
  • Build graph:
    g = {2: [3], 3: [2]}
    s = {2, 3}
  • Find who knows the secret in this group: person 2
  • BFS from person 2:
    • Visit person 2 β†’ spread to person 3
    • Mark vis[3] = True
  • After time 8: vis = [True, True, True, True, False, False]

Step 4: Process Time 10 (meeting at index 3)

  • Meeting: [1,5,10]
  • Build graph:
    g = {1: [5], 5: [1]}
    s = {1, 5}
  • Find who knows the secret in this group: person 1
  • BFS from person 1:
    • Visit person 1 β†’ spread to person 5
    • Mark vis[5] = True
  • After time 10: vis = [True, True, True, True, False, True]

Final Result: People who know the secret are [0, 1, 2, 3, 5]

Note how person 4 never learns the secret because they only met person 3 at time 5, before person 3 knew the secret. This demonstrates the importance of processing meetings chronologically.

Solution Implementation

1from collections import defaultdict, deque
2from typing import List
3
4class Solution:
5    def findAllPeople(
6        self, n: int, meetings: List[List[int]], firstPerson: int
7    ) -> List[int]:
8        # Track who knows the secret (initially person 0 and firstPerson)
9        knows_secret = [False] * n
10        knows_secret[0] = True
11        knows_secret[firstPerson] = True
12      
13        # Sort meetings by time to process them chronologically
14        meetings.sort(key=lambda meeting: meeting[2])
15      
16        meeting_idx = 0
17        total_meetings = len(meetings)
18      
19        # Process all meetings grouped by time
20        while meeting_idx < total_meetings:
21            # Find all meetings happening at the same time
22            end_idx = meeting_idx
23            current_time = meetings[meeting_idx][2]
24          
25            while end_idx + 1 < total_meetings and meetings[end_idx + 1][2] == current_time:
26                end_idx += 1
27          
28            # Build graph for people meeting at this time
29            people_at_current_time = set()
30            adjacency_list = defaultdict(list)
31          
32            for person_x, person_y, _ in meetings[meeting_idx : end_idx + 1]:
33                # Create bidirectional edges for people meeting
34                adjacency_list[person_x].append(person_y)
35                adjacency_list[person_y].append(person_x)
36                people_at_current_time.add(person_x)
37                people_at_current_time.add(person_y)
38          
39            # BFS to spread the secret among connected people at this time
40            queue = deque()
41          
42            # Start BFS from people who already know the secret
43            for person in people_at_current_time:
44                if knows_secret[person]:
45                    queue.append(person)
46          
47            # Spread the secret through the connected component
48            while queue:
49                current_person = queue.popleft()
50              
51                for neighbor in adjacency_list[current_person]:
52                    if not knows_secret[neighbor]:
53                        knows_secret[neighbor] = True
54                        queue.append(neighbor)
55          
56            # Move to next time group
57            meeting_idx = end_idx + 1
58      
59        # Return list of all people who know the secret
60        return [person_id for person_id in range(n) if knows_secret[person_id]]
61
1class Solution {
2    public List<Integer> findAllPeople(int n, int[][] meetings, int firstPerson) {
3        // Track which people know the secret
4        boolean[] knowsSecret = new boolean[n];
5        knowsSecret[0] = true;  // Person 0 initially knows the secret
6        knowsSecret[firstPerson] = true;  // firstPerson initially knows the secret
7      
8        // Sort meetings by time
9        int totalMeetings = meetings.length;
10        Arrays.sort(meetings, Comparator.comparingInt(meeting -> meeting[2]));
11      
12        // Process meetings grouped by time
13        for (int i = 0; i < totalMeetings;) {
14            // Find all meetings happening at the same time
15            int endIndex = i;
16            while (endIndex + 1 < totalMeetings && 
17                   meetings[endIndex + 1][2] == meetings[i][2]) {
18                endIndex++;
19            }
20          
21            // Build adjacency list for people meeting at this time
22            Map<Integer, List<Integer>> adjacencyList = new HashMap<>();
23            Set<Integer> peopleInCurrentTime = new HashSet<>();
24          
25            for (int k = i; k <= endIndex; k++) {
26                int person1 = meetings[k][0];
27                int person2 = meetings[k][1];
28              
29                // Create bidirectional edges
30                adjacencyList.computeIfAbsent(person1, key -> new ArrayList<>()).add(person2);
31                adjacencyList.computeIfAbsent(person2, key -> new ArrayList<>()).add(person1);
32              
33                // Track all people involved in meetings at this time
34                peopleInCurrentTime.add(person1);
35                peopleInCurrentTime.add(person2);
36            }
37          
38            // BFS to spread the secret among connected people at this time
39            Deque<Integer> queue = new ArrayDeque<>();
40          
41            // Start BFS from people who already know the secret
42            for (int person : peopleInCurrentTime) {
43                if (knowsSecret[person]) {
44                    queue.offer(person);
45                }
46            }
47          
48            // Spread the secret through the connected graph
49            while (!queue.isEmpty()) {
50                int currentPerson = queue.poll();
51              
52                // Share secret with all neighbors
53                for (int neighbor : adjacencyList.getOrDefault(currentPerson, Collections.emptyList())) {
54                    if (!knowsSecret[neighbor]) {
55                        knowsSecret[neighbor] = true;
56                        queue.offer(neighbor);
57                    }
58                }
59            }
60          
61            // Move to next time group
62            i = endIndex + 1;
63        }
64      
65        // Collect all people who know the secret
66        List<Integer> result = new ArrayList<>();
67        for (int person = 0; person < n; person++) {
68            if (knowsSecret[person]) {
69                result.add(person);
70            }
71        }
72      
73        return result;
74    }
75}
76
1class Solution {
2public:
3    vector<int> findAllPeople(int n, vector<vector<int>>& meetings, int firstPerson) {
4        // Track which people know the secret
5        vector<bool> knowsSecret(n);
6        knowsSecret[0] = true;           // Person 0 initially knows
7        knowsSecret[firstPerson] = true; // firstPerson initially knows
8      
9        // Sort meetings by time
10        sort(meetings.begin(), meetings.end(), [](const auto& a, const auto& b) {
11            return a[2] < b[2];
12        });
13      
14        int totalMeetings = meetings.size();
15        int currentIndex = 0;
16      
17        // Process meetings grouped by same time
18        while (currentIndex < totalMeetings) {
19            // Find the range of meetings happening at the same time
20            int endIndex = currentIndex;
21            while (endIndex + 1 < totalMeetings && 
22                   meetings[endIndex + 1][2] == meetings[currentIndex][2]) {
23                endIndex++;
24            }
25          
26            // Build adjacency list for people meeting at this time
27            unordered_map<int, vector<int>> adjacencyList;
28            unordered_set<int> peopleInCurrentTime;
29          
30            // Add all meetings at current time to the graph
31            for (int k = currentIndex; k <= endIndex; k++) {
32                int person1 = meetings[k][0];
33                int person2 = meetings[k][1];
34              
35                adjacencyList[person1].push_back(person2);
36                adjacencyList[person2].push_back(person1);
37                peopleInCurrentTime.insert(person1);
38                peopleInCurrentTime.insert(person2);
39            }
40          
41            // BFS to spread the secret among connected people at this time
42            queue<int> bfsQueue;
43          
44            // Start BFS from people who already know the secret
45            for (int person : peopleInCurrentTime) {
46                if (knowsSecret[person]) {
47                    bfsQueue.push(person);
48                }
49            }
50          
51            // Spread the secret through connected components
52            while (!bfsQueue.empty()) {
53                int currentPerson = bfsQueue.front();
54                bfsQueue.pop();
55              
56                // Share secret with all people meeting currentPerson
57                for (int neighbor : adjacencyList[currentPerson]) {
58                    if (!knowsSecret[neighbor]) {
59                        knowsSecret[neighbor] = true;
60                        bfsQueue.push(neighbor);
61                    }
62                }
63            }
64          
65            // Move to next time group
66            currentIndex = endIndex + 1;
67        }
68      
69        // Collect all people who know the secret
70        vector<int> result;
71        for (int i = 0; i < n; i++) {
72            if (knowsSecret[i]) {
73                result.push_back(i);
74            }
75        }
76      
77        return result;
78    }
79};
80
1/**
2 * Finds all people who know the secret after all meetings
3 * @param n - Total number of people (0 to n-1)
4 * @param meetings - Array of meetings [person1, person2, time]
5 * @param firstPerson - The first person who knows the secret (besides person 0)
6 * @returns Array of all people who know the secret
7 */
8function findAllPeople(n: number, meetings: number[][], firstPerson: number): number[] {
9    // Initialize Union-Find parent array where each person is their own parent initially
10    const parent: number[] = Array.from({ length: n + 1 }, (_, index) => index);
11  
12    // Person 0 knows the secret initially, firstPerson also knows it
13    parent[firstPerson] = 0;
14
15    /**
16     * Find the root parent of a person using path compression
17     * @param index - The person's index
18     * @returns The root parent of the person
19     */
20    function findParent(index: number): number {
21        if (parent[index] !== index) {
22            // Path compression: directly connect to root parent
23            parent[index] = findParent(parent[index]);
24        }
25        return parent[index];
26    }
27
28    // Group meetings by time
29    const meetingsByTime = new Map<number, number[][]>();
30  
31    for (const meeting of meetings) {
32        const [person1, person2, time] = meeting;
33      
34        if (!meetingsByTime.has(time)) {
35            meetingsByTime.set(time, []);
36        }
37        meetingsByTime.get(time)!.push([person1, person2]);
38    }
39  
40    // Process meetings in chronological order
41    const sortedTimes = Array.from(meetingsByTime.keys()).sort((a, b) => a - b);
42  
43    for (const time of sortedTimes) {
44        const currentMeetings = meetingsByTime.get(time)!;
45      
46        // First pass: Union people who meet at this time
47        for (const [personA, personB] of currentMeetings) {
48            const rootA = findParent(personA);
49            const rootB = findParent(personB);
50          
51            // If either person knows the secret (parent is 0), share it
52            if (parent[rootA] === 0 || parent[rootB] === 0) {
53                parent[rootA] = 0;
54                parent[rootB] = 0;
55            }
56          
57            // Union the two groups
58            parent[rootA] = parent[rootB];
59        }
60      
61        // Second pass: Reset people who don't know the secret after this time
62        for (const [personA, personB] of currentMeetings) {
63            const rootA = findParent(personA);
64            const rootB = findParent(personB);
65          
66            // If they know the secret, keep the connection
67            if (parent[rootA] === 0 || parent[rootB] === 0) {
68                parent[rootA] = 0;
69                parent[rootB] = 0;
70            } else {
71                // Reset connections for people who don't know the secret
72                parent[personA] = personA;
73                parent[personB] = personB;
74            }
75        }
76    }
77
78    // Collect all people who know the secret
79    const result: number[] = [];
80  
81    for (let i = 0; i <= n; i++) {
82        // A person knows the secret if their root parent is 0
83        if (parent[findParent(i)] === 0) {
84            result.push(i);
85        }
86    }
87  
88    return result;
89}
90

Time and Space Complexity

Time Complexity: O(m log m + m + E) where m is the number of meetings and E is the total number of edges across all time groups.

  • Sorting the meetings takes O(m log m)
  • The outer while loop processes each meeting exactly once: O(m)
  • For each time group, we:
    • Build the adjacency list: O(k) where k is the number of meetings in that time group
    • Perform BFS traversal: O(V + E) where V is the number of unique people in that time group and E is the number of edges (connections) in that time group
  • Since each meeting is processed once and contributes to exactly one time group's graph, the total BFS work across all time groups is O(m + E) where E can be at most 2m (since each meeting creates at most 2 directed edges)
  • Overall: O(m log m + m + E) = O(m log m + m) = O(m log m)

Space Complexity: O(n + m)

  • vis array: O(n)
  • For each time group, we create:
    • Set s: O(V) where V is the number of unique people in that time group
    • Graph g: O(V + E) where E is the number of edges in that time group
    • Queue q: O(V) in the worst case
  • Since we process one time group at a time and the temporary structures are recreated for each group, the maximum space used at any point is O(n) for the vis array plus O(V + E) for the largest time group
  • In the worst case, all meetings could happen at the same time, making V = O(n) and E = O(m)
  • Overall: O(n + m)

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Not Handling Simultaneous Secret Propagation Within Same Time

The Pitfall: A common mistake is to process meetings at the same time individually rather than as a group. If you process meetings one by one, you might miss the chain reaction of secret spreading that should happen instantaneously.

Example Scenario:

meetings = [[1, 2, 5], [2, 3, 5], [3, 4, 5]]
# If person 1 knows the secret:
# - Person 1 meets 2 at time 5 (2 learns secret)
# - Person 2 meets 3 at time 5 (3 should learn secret immediately)
# - Person 3 meets 4 at time 5 (4 should learn secret immediately)

Wrong Approach:

# Processing meetings individually
for x, y, time in meetings:
    if knows_secret[x]:
        knows_secret[y] = True
    elif knows_secret[y]:
        knows_secret[x] = True
# This fails because person 2 learns at time 5 but can't immediately share with person 3

Correct Solution: Group all meetings by time and use BFS to propagate the secret through the entire connected component at once, as shown in the provided solution.

2. Forgetting to Sort Meetings by Time

The Pitfall: Processing meetings in arbitrary order instead of chronological order means people who learn the secret later might not be able to share it in their subsequent meetings.

Example:

meetings = [[1, 2, 10], [0, 1, 5]]  # Unsorted
# If processed as-is:
# - Meeting at time 10: persons 1 and 2 meet (neither knows secret yet)
# - Meeting at time 5: person 0 shares with person 1
# Person 2 never learns the secret!

Solution: Always sort meetings by timestamp before processing:

meetings.sort(key=lambda meeting: meeting[2])

3. Creating a Global Graph Instead of Per-Timestamp Graphs

The Pitfall: Building one graph for all meetings ignores the temporal aspect - people can only share secrets during their actual meeting times.

Wrong Approach:

# Building a single graph for all meetings
graph = defaultdict(list)
for x, y, _ in meetings:
    graph[x].append(y)
    graph[y].append(x)
# Then doing one BFS - this incorrectly allows secret sharing across different times

Correct Solution: Create a fresh graph for each timestamp group and only propagate the secret among people meeting at that specific time.

4. Not Maintaining Persistent Knowledge State

The Pitfall: Resetting or not properly maintaining the knows_secret array between different timestamps means people "forget" the secret they learned earlier.

Example of Wrong Implementation:

while meeting_idx < total_meetings:
    # Wrong: Creating new array for each time group
    temp_knows = knows_secret.copy()  
    # ... BFS propagation ...
    knows_secret = temp_knows  # This might lose information

Correct Approach: Use a single knows_secret array throughout the entire algorithm that accumulates knowledge over time. Once someone learns the secret, they keep it forever.

5. Incorrect BFS Initialization

The Pitfall: Starting BFS from all people in meetings rather than only those who already know the secret.

Wrong:

queue = deque(people_at_current_time)  # Start with everyone

Correct:

queue = deque([person for person in people_at_current_time if knows_secret[person]])

Only people who already know the secret can spread it to others in their meetings.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

What data structure does Breadth-first search typically uses to store intermediate states?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More