Facebook Pixel

1733. Minimum Number of People to Teach

MediumGreedyArrayHash Table
Leetcode Link

Problem Description

You have a social network with m users where users need a common language to communicate with each other.

Given:

  • n: The total number of languages (numbered from 1 to n)
  • languages[i]: An array where each element is a list of languages that user i knows (users are 0-indexed in the array but 1-indexed in friendships)
  • friendships[i] = [ui, vi]: An array of friendship pairs between users

The problem is asking you to find the minimum number of users you need to teach one single language so that all friend pairs can communicate.

Key points to understand:

  • Two users can communicate if they share at least one common language
  • You can only choose ONE language to teach to selected users
  • The goal is to ensure every friendship pair has at least one common language
  • Friendships are not transitive (if x is friends with y, and y is friends with z, x and z are not necessarily friends)

For example, if user 1 knows languages [1, 2] and user 2 knows languages [3, 4], and they are friends, they cannot communicate. You would need to teach either language 1, 2, 3, or 4 to at least one of them so they can communicate.

The solution approach involves:

  1. First identifying which friendship pairs cannot currently communicate (no common language)
  2. Collecting all users from these "problematic" friendships into a set
  3. For each language, counting how many of these problematic users already know it
  4. The answer is the total number of problematic users minus the maximum count from step 3 (choosing the language that the most users already know minimizes teaching)
Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is that we only need to focus on the friendship pairs that currently cannot communicate - these are the "problematic" pairs where the two users share no common language.

Think about it this way: if two friends already share a language, they can communicate, so we don't need to teach them anything. We only need to worry about pairs that can't communicate.

Once we identify all the users involved in these problematic friendships, we need to choose ONE language to teach to some of them. Which language should we choose?

Here's the clever part: if we choose a language that many of these problematic users already know, we'll need to teach fewer people. For example, if we have 10 problematic users and 7 of them already know language #3, then we only need to teach language #3 to the remaining 3 users.

So our strategy becomes:

  1. Find all users who are in at least one "cannot communicate" friendship - let's say there are total such users
  2. For each language, count how many of these total users already know it
  3. Choose the language with the maximum count - let's call this max_count
  4. The answer is total - max_count because we need to teach this language to everyone who doesn't already know it

This greedy approach works because teaching the most popular language (among the problematic users) minimizes the number of people we need to teach. It's like choosing the most common denominator - we want to leverage what people already know as much as possible.

Learn more about Greedy patterns.

Solution Approach

Let's walk through the implementation step by step:

Step 1: Identify Problematic Friendships

First, we need a helper function check(u, v) that determines if two users can communicate:

  • It iterates through all languages known by user u and user v
  • If any language matches, they can communicate (returns True)
  • Otherwise, they cannot communicate (returns False)

Step 2: Collect Problematic Users

We create a set s to store all users who are involved in at least one friendship where communication is not possible:

s = set()
for u, v in friendships:
    if not check(u, v):
        s.add(u)
        s.add(v)

Using a set ensures each user is only counted once, even if they're involved in multiple problematic friendships.

Step 3: Count Language Frequencies

For each user in our problematic set s, we count how many users know each language:

cnt = Counter()
for u in s:
    for l in languages[u - 1]:
        cnt[l] += 1

Note: We use u - 1 because users are 1-indexed in friendships but 0-indexed in the languages array.

Step 4: Calculate the Answer

The minimum number of users to teach is:

return len(s) - max(cnt.values(), default=0)
  • len(s) is the total number of problematic users
  • max(cnt.values()) is the maximum count of any language among these users
  • The difference gives us the minimum number of users who need to learn the most popular language
  • We use default=0 to handle the edge case where all friendships can already communicate (empty set s)

This greedy approach ensures we teach the fewest possible users by selecting the language that the most problematic users already know.

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 concrete example to illustrate the solution approach.

Given:

  • n = 3 (languages numbered 1, 2, 3)
  • languages = [[1], [2], [1,2], [3]]
    • User 1 knows: language 1
    • User 2 knows: language 2
    • User 3 knows: languages 1 and 2
    • User 4 knows: language 3
  • friendships = [[1,2], [2,4], [3,4]]

Step 1: Identify Problematic Friendships

Let's check each friendship pair:

  • Friendship [1,2]: User 1 knows {1}, User 2 knows {2} β†’ No common language ❌
  • Friendship [2,4]: User 2 knows {2}, User 4 knows {3} β†’ No common language ❌
  • Friendship [3,4]: User 3 knows {1,2}, User 4 knows {3} β†’ No common language ❌

All three friendships are problematic!

Step 2: Collect Problematic Users

From the problematic friendships, we collect all involved users:

  • From [1,2]: Add users 1 and 2
  • From [2,4]: Add users 2 and 4 (user 2 already added)
  • From [3,4]: Add users 3 and 4 (user 4 already added)

Problematic users set s = {1, 2, 3, 4} (all 4 users!)

Step 3: Count Language Frequencies

Count how many problematic users know each language:

  • Language 1: User 1 knows it, User 3 knows it β†’ count = 2
  • Language 2: User 2 knows it, User 3 knows it β†’ count = 2
  • Language 3: User 4 knows it β†’ count = 1

Language frequency: {1: 2, 2: 2, 3: 1}

Step 4: Calculate the Answer

  • Total problematic users = 4
  • Maximum language count = max(2, 2, 1) = 2
  • Users to teach = 4 - 2 = 2

Verification:

If we choose language 1 (known by 2 users already):

  • Teach language 1 to users 2 and 4
  • Now: [1,2] can communicate via language 1 βœ“
  • Now: [2,4] can communicate via language 1 βœ“
  • Now: [3,4] can communicate via language 1 βœ“

Similarly, if we choose language 2, we'd teach it to users 1 and 4. Either way, we need to teach exactly 2 users!

Solution Implementation

1class Solution:
2    def minimumTeachings(
3        self, n: int, languages: List[List[int]], friendships: List[List[int]]
4    ) -> int:
5        """
6        Find the minimum number of users who need to learn a new language
7        so that all friend pairs can communicate.
8      
9        Args:
10            n: Total number of available languages (1 to n)
11            languages: List where languages[i] contains languages known by user i+1
12            friendships: List of [user1, user2] pairs representing friendships
13      
14        Returns:
15            Minimum number of users who need to learn a new language
16        """
17      
18        def can_communicate(user1: int, user2: int) -> bool:
19            """
20            Check if two users can communicate (share at least one common language).
21          
22            Args:
23                user1: First user ID (1-indexed)
24                user2: Second user ID (1-indexed)
25          
26            Returns:
27                True if users share a common language, False otherwise
28            """
29            # Get languages for both users (convert to 0-indexed)
30            user1_languages = languages[user1 - 1]
31            user2_languages = languages[user2 - 1]
32          
33            # Check if any language is common between the two users
34            for lang1 in user1_languages:
35                for lang2 in user2_languages:
36                    if lang1 == lang2:
37                        return True
38            return False
39      
40        # Find all users who are in friendships that cannot communicate
41        users_needing_language = set()
42        for user1, user2 in friendships:
43            if not can_communicate(user1, user2):
44                # Both users in this friendship need to learn a common language
45                users_needing_language.add(user1)
46                users_needing_language.add(user2)
47      
48        # Count how many problematic users already know each language
49        language_count = Counter()
50        for user in users_needing_language:
51            # For each user who needs to communicate, count their known languages
52            for language_id in languages[user - 1]:
53                language_count[language_id] += 1
54      
55        # The minimum teachings needed is:
56        # Total problematic users - Maximum users who already know the same language
57        # If we teach the most commonly known language to those who don't know it,
58        # we minimize the number of teachings
59        if language_count:
60            return len(users_needing_language) - max(language_count.values())
61        else:
62            # If no problematic users exist, no teaching is needed
63            return 0
64
1class Solution {
2    /**
3     * Finds the minimum number of people to teach a new language so that
4     * all friendships can communicate.
5     * 
6     * @param n           The total number of languages available (1 to n)
7     * @param languages   2D array where languages[i] contains languages known by person i+1
8     * @param friendships 2D array where each element [u, v] represents a friendship
9     * @return The minimum number of people that need to be taught a new language
10     */
11    public int minimumTeachings(int n, int[][] languages, int[][] friendships) {
12        // Set to store people who cannot communicate with at least one friend
13        Set<Integer> peopleNeedingHelp = new HashSet<>();
14      
15        // Check each friendship pair to find those who cannot communicate
16        for (int[] friendship : friendships) {
17            int person1 = friendship[0];
18            int person2 = friendship[1];
19          
20            // If these two friends don't share a common language
21            if (!haveCommonLanguage(person1, person2, languages)) {
22                peopleNeedingHelp.add(person1);
23                peopleNeedingHelp.add(person2);
24            }
25        }
26      
27        // If everyone can already communicate, no teaching needed
28        if (peopleNeedingHelp.isEmpty()) {
29            return 0;
30        }
31      
32        // Count how many people needing help already know each language
33        int[] languageCount = new int[n + 1];  // Index 0 unused, languages are 1 to n
34      
35        for (int person : peopleNeedingHelp) {
36            // For each language this person knows
37            for (int language : languages[person - 1]) {  // person-1 because array is 0-indexed
38                languageCount[language]++;
39            }
40        }
41      
42        // Find the language known by the most people who need help
43        int maxPeopleKnowingLanguage = 0;
44        for (int count : languageCount) {
45            maxPeopleKnowingLanguage = Math.max(maxPeopleKnowingLanguage, count);
46        }
47      
48        // The minimum people to teach = total people needing help - those who already know the best language
49        return peopleNeedingHelp.size() - maxPeopleKnowingLanguage;
50    }
51  
52    /**
53     * Checks if two people share at least one common language.
54     * 
55     * @param person1   First person (1-indexed)
56     * @param person2   Second person (1-indexed)
57     * @param languages 2D array of languages known by each person
58     * @return true if they share a common language, false otherwise
59     */
60    private boolean haveCommonLanguage(int person1, int person2, int[][] languages) {
61        // Check each language of person1 against each language of person2
62        for (int language1 : languages[person1 - 1]) {  // person1-1 for 0-indexed array
63            for (int language2 : languages[person2 - 1]) {  // person2-1 for 0-indexed array
64                if (language1 == language2) {
65                    return true;  // Found a common language
66                }
67            }
68        }
69        return false;  // No common language found
70    }
71}
72
1class Solution {
2public:
3    int minimumTeachings(int n, vector<vector<int>>& languages, vector<vector<int>>& friendships) {
4        // Set to store users who cannot communicate with at least one friend
5        unordered_set<int> usersNeedingHelp;
6      
7        // Check each friendship pair
8        for (auto& friendship : friendships) {
9            int user1 = friendship[0];
10            int user2 = friendship[1];
11          
12            // If these two users don't share a common language, add both to the set
13            if (!haveCommonLanguage(user1, user2, languages)) {
14                usersNeedingHelp.insert(user1);
15                usersNeedingHelp.insert(user2);
16            }
17        }
18      
19        // If all friends can already communicate, no teaching needed
20        if (usersNeedingHelp.empty()) {
21            return 0;
22        }
23      
24        // Count how many users needing help already know each language
25        vector<int> languageCount(n + 1, 0);  // languageCount[i] = number of users who know language i
26      
27        for (int userId : usersNeedingHelp) {
28            // For each user needing help, count the languages they already know
29            for (int& languageId : languages[userId - 1]) {  // userId - 1 because users are 1-indexed
30                ++languageCount[languageId];
31            }
32        }
33      
34        // Find the language known by the most users who need help
35        int maxUsersKnowingLanguage = *max_element(languageCount.begin(), languageCount.end());
36      
37        // Minimum teachings = total users needing help - users who already know the most popular language
38        return usersNeedingHelp.size() - maxUsersKnowingLanguage;
39    }
40
41private:
42    // Helper function to check if two users share at least one common language
43    bool haveCommonLanguage(int user1, int user2, vector<vector<int>>& languages) {
44        // Check if any language of user1 matches any language of user2
45        for (int language1 : languages[user1 - 1]) {  // user1 - 1 because users are 1-indexed
46            for (int language2 : languages[user2 - 1]) {  // user2 - 1 because users are 1-indexed
47                if (language1 == language2) {
48                    return true;  // Found a common language
49                }
50            }
51        }
52        return false;  // No common language found
53    }
54};
55
1/**
2 * Finds the minimum number of users that need to be taught a new language
3 * so that all friend pairs can communicate
4 * @param n - Total number of languages available (1 to n)
5 * @param languages - Array where languages[i] contains the languages known by user i+1
6 * @param friendships - Array of friend pairs [user1, user2]
7 * @returns Minimum number of users that need to learn a new language
8 */
9function minimumTeachings(n: number, languages: number[][], friendships: number[][]): number {
10    // Set to store users who cannot communicate with at least one friend
11    const usersNeedingHelp = new Set<number>();
12  
13    // Check each friendship pair to identify users who cannot communicate
14    for (const friendship of friendships) {
15        const user1 = friendship[0];
16        const user2 = friendship[1];
17      
18        // If these two users don't share a common language, add both to the set
19        if (!haveCommonLanguage(user1, user2, languages)) {
20            usersNeedingHelp.add(user1);
21            usersNeedingHelp.add(user2);
22        }
23    }
24  
25    // If all friends can already communicate, no teaching needed
26    if (usersNeedingHelp.size === 0) {
27        return 0;
28    }
29  
30    // Count how many users needing help already know each language
31    // languageCount[i] = number of users who know language i
32    const languageCount = new Array(n + 1).fill(0);
33  
34    // For each user needing help, count the languages they already know
35    for (const userId of usersNeedingHelp) {
36        // Iterate through languages known by this user (userId - 1 because users are 1-indexed)
37        for (const languageId of languages[userId - 1]) {
38            languageCount[languageId]++;
39        }
40    }
41  
42    // Find the language known by the most users who need help
43    const maxUsersKnowingLanguage = Math.max(...languageCount);
44  
45    // Minimum teachings = total users needing help - users who already know the most popular language
46    return usersNeedingHelp.size - maxUsersKnowingLanguage;
47}
48
49/**
50 * Helper function to check if two users share at least one common language
51 * @param user1 - First user ID (1-indexed)
52 * @param user2 - Second user ID (1-indexed)
53 * @param languages - Array where languages[i] contains the languages known by user i+1
54 * @returns true if users share at least one common language, false otherwise
55 */
56function haveCommonLanguage(user1: number, user2: number, languages: number[][]): boolean {
57    // Get languages known by each user (subtract 1 because users are 1-indexed)
58    const languages1 = languages[user1 - 1];
59    const languages2 = languages[user2 - 1];
60  
61    // Check if any language of user1 matches any language of user2
62    for (const language1 of languages1) {
63        for (const language2 of languages2) {
64            if (language1 === language2) {
65                return true;  // Found a common language
66            }
67        }
68    }
69  
70    return false;  // No common language found
71}
72

Time and Space Complexity

Time Complexity: O(k Γ— mΒ² + k Γ— m)

Breaking down the analysis:

  • The check function has nested loops that iterate through languages of two users, which takes O(mΒ²) in the worst case where m is the maximum number of languages a person can know
  • The first main loop iterates through all friendships (k iterations) and calls check for each, resulting in O(k Γ— mΒ²)
  • Building set s takes O(k) in the worst case
  • The second loop iterates through users in set s (at most 2k users) and their languages (at most m each), contributing O(k Γ— m)
  • Finding the maximum value in the counter takes O(n) where n is the total number of languages
  • Overall: O(k Γ— mΒ² + k Γ— m + n), which simplifies to O(k Γ— mΒ²) as the dominant term

Space Complexity: O(k + n)

  • Set s stores at most 2k users (two per friendship): O(k)
  • Counter cnt stores at most n language counts: O(n)
  • Total space: O(k + n)

Note: The reference answer states O(mΒ² Γ— k), which appears to be the same as my analysis O(k Γ— mΒ²) with the terms reordered.

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

Common Pitfalls

1. Incorrectly Checking All Languages Instead of Just Problematic Users

A common mistake is to count language frequencies across ALL users in the network, rather than just the users involved in problematic friendships.

Incorrect approach:

# WRONG: Counting languages for all users
language_count = Counter()
for i, user_languages in enumerate(languages):
    for lang in user_languages:
        language_count[lang] += 1

Why it's wrong: We only need to teach languages to users who are in friendships that cannot communicate. Teaching a language to users who can already communicate with all their friends is unnecessary.

Correct approach:

# RIGHT: Only count languages for users in problematic friendships
language_count = Counter()
for user in users_needing_language:  # Only problematic users
    for language_id in languages[user - 1]:
        language_count[language_id] += 1

2. Index Confusion Between User IDs and Array Indices

Users are 1-indexed in the friendships array but the languages array is 0-indexed. Forgetting to convert between these can cause index errors or incorrect results.

Incorrect approach:

# WRONG: Using user ID directly as array index
for user in users_needing_language:
    for language_id in languages[user]:  # Missing the -1 adjustment
        language_count[language_id] += 1

Correct approach:

# RIGHT: Convert 1-indexed user ID to 0-indexed array position
for user in users_needing_language:
    for language_id in languages[user - 1]:  # Proper index adjustment
        language_count[language_id] += 1

3. Trying to Optimize Language Selection Per Friendship Pair

Some might try to find the optimal language for each friendship pair individually, but the problem requires choosing ONE single language to teach globally.

Incorrect approach:

# WRONG: Trying to solve each friendship independently
total_teachings = 0
for u, v in friendships:
    if not can_communicate(u, v):
        # Find best language for this pair only
        best_for_pair = find_best_language(u, v)
        total_teachings += teach_language(best_for_pair, u, v)

Correct approach: The solution must choose one language that works best globally for all problematic friendships, not optimize each friendship individually.

4. Using Set Operations Incorrectly for Language Comparison

When checking if users can communicate, using set operations without proper conversion can lead to errors.

Potentially problematic approach:

# May be inefficient or error-prone with certain inputs
def can_communicate(user1, user2):
    return bool(set(languages[user1-1]) & set(languages[user2-1]))

While this works, it creates unnecessary set objects for every check. The nested loop approach in the solution is clearer and avoids repeated set creation for frequently checked users.

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