Facebook Pixel

2923. Find Champion I

Problem Description

You are given n teams numbered from 0 to n - 1 participating in a tournament. The results of all matches between teams are represented in a 2D boolean matrix grid of size n × n.

For any two different teams i and j (where 0 <= i, j <= n - 1 and i != j):

  • If grid[i][j] == 1, then team i is stronger than team j (team i beat team j)
  • If grid[i][j] == 0, then team j is stronger than team i (team j beat team i)

A team becomes the champion of the tournament if no other team is stronger than it. In other words, a team is the champion if it has beaten all other teams.

Your task is to find and return the team number that will be the champion of the tournament.

The solution works by checking each team i to see if it has beaten all other teams. For a team to be the champion, all values in its row (except the diagonal position grid[i][i]) must be 1, indicating it has won against every other team. The first team that satisfies this condition is returned as the champion.

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

Intuition

To find the champion, we need to identify a team that has beaten every other team in the tournament.

Think about what the grid represents: grid[i][j] = 1 means team i beat team j. So if we look at row i in the grid, it tells us all the match results for team i against other teams.

For team i to be the champion, it must have won against all other teams. This means row i should contain 1 in all positions except position i itself (since a team doesn't play against itself).

For example, if we have 3 teams and team 0 is the champion, then row 0 would look like: [?, 1, 1] where the first position doesn't matter since it represents team 0 vs team 0.

This leads us to a straightforward approach: examine each row of the grid. If we find a row where all values are 1 (except the diagonal position where i == j), then that team is undefeated and therefore the champion.

The key insight is that we're guaranteed to have exactly one champion based on the problem constraints (the tournament has a definitive winner), so we can return immediately once we find a team that has beaten all others.

Solution Approach

The solution uses a simple enumeration approach to find the champion team.

We iterate through each team i (which corresponds to each row in the grid) using enumerate(grid). For each team, we check if it has beaten all other teams.

The key implementation detail is the condition check: all(x == 1 for j, x in enumerate(row) if i != j)

Breaking this down:

  • enumerate(row) gives us each element x in the current row along with its index j
  • if i != j ensures we skip the diagonal element (team i vs itself)
  • x == 1 checks if team i beat team j
  • all(...) returns True only if team i beat every other team

If a team i satisfies this condition (meaning all non-diagonal elements in its row are 1), then team i is the champion and we immediately return i.

The algorithm has a time complexity of O(n²) in the worst case, as we might need to check all n rows, and for each row, we check n-1 elements. The space complexity is O(1) as we only use a constant amount of extra space for iteration variables.

The solution leverages Python's built-in all() function for concise code, which short-circuits and returns False as soon as it encounters a team that the current team hasn't beaten, making it efficient in practice.

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 with 4 teams to illustrate how the solution finds the champion.

Given the following grid:

grid = [[0, 0, 1, 1],
        [1, 0, 1, 1],
        [0, 0, 0, 1],
        [0, 0, 0, 0]]

Step 1: Check Team 0 (Row 0)

  • Row 0: [0, 0, 1, 1]
  • Check positions where i != j:
    • Position 1: grid[0][1] = 0 (Team 0 lost to Team 1) ❌
  • Team 0 is not the champion since it lost to Team 1

Step 2: Check Team 1 (Row 1)

  • Row 1: [1, 0, 1, 1]
  • Check positions where i != j:
    • Position 0: grid[1][0] = 1 (Team 1 beat Team 0) ✓
    • Position 2: grid[1][2] = 1 (Team 1 beat Team 2) ✓
    • Position 3: grid[1][3] = 1 (Team 1 beat Team 3) ✓
  • All non-diagonal elements are 1, so Team 1 beat all other teams
  • Team 1 is the champion! Return 1

The algorithm stops here and returns 1. For completeness, let's verify our answer:

  • Team 1 beat Team 0 (grid[1][0] = 1)
  • Team 1 beat Team 2 (grid[1][2] = 1)
  • Team 1 beat Team 3 (grid[1][3] = 1)

Indeed, Team 1 has defeated all other teams and is the tournament champion.

Solution Implementation

1class Solution:
2    def findChampion(self, grid: List[List[int]]) -> int:
3        """
4        Find the champion team that is stronger than all other teams.
5      
6        Args:
7            grid: 2D list where grid[i][j] = 1 if team i beats team j, else 0
8          
9        Returns:
10            Index of the champion team
11        """
12        # Iterate through each team (row) in the grid
13        for team_index, team_row in enumerate(grid):
14            # Check if current team beats all other teams
15            # A team is champion if it has value 1 for all positions except itself
16            is_champion = all(
17                value == 1 
18                for opponent_index, value in enumerate(team_row) 
19                if team_index != opponent_index
20            )
21          
22            # If this team beats all others, it's the champion
23            if is_champion:
24                return team_index
25
1class Solution {
2    /**
3     * Finds the champion team in a tournament grid.
4     * A team is champion if it beats all other teams (has 1s in all other positions in its row).
5     * 
6     * @param grid A 2D array where grid[i][j] = 1 if team i beats team j, 0 otherwise
7     * @return The index of the champion team
8     */
9    public int findChampion(int[][] grid) {
10        int n = grid.length;
11      
12        // Iterate through each team to check if it's the champion
13        for (int teamIndex = 0; teamIndex < n; teamIndex++) {
14            int winsCount = 0;
15          
16            // Count how many teams the current team beats
17            for (int opponentIndex = 0; opponentIndex < n; opponentIndex++) {
18                // Skip when comparing team with itself
19                if (teamIndex != opponentIndex && grid[teamIndex][opponentIndex] == 1) {
20                    winsCount++;
21                }
22            }
23          
24            // A champion must beat all other teams (n-1 teams)
25            if (winsCount == n - 1) {
26                return teamIndex;
27            }
28        }
29      
30        // This line should never be reached if input guarantees a champion exists
31        return -1;
32    }
33}
34
1class Solution {
2public:
3    int findChampion(vector<vector<int>>& grid) {
4        int n = grid.size();
5      
6        // Iterate through each potential champion (row index)
7        for (int i = 0; i < n; ++i) {
8            int winCount = 0;
9          
10            // Count how many teams this team beats
11            for (int j = 0; j < n; ++j) {
12                // Skip self-comparison and count wins (grid[i][j] == 1 means team i beats team j)
13                if (i != j && grid[i][j] == 1) {
14                    ++winCount;
15                }
16            }
17          
18            // A champion must beat all other teams (n - 1 teams)
19            if (winCount == n - 1) {
20                return i;
21            }
22        }
23      
24        // This line should never be reached if input guarantees a champion exists
25        return -1;
26    }
27};
28
1/**
2 * Finds the champion team in a tournament grid.
3 * A team is a champion if it beats all other teams (has n-1 wins).
4 * 
5 * @param grid - 2D array where grid[i][j] = 1 if team i beats team j, 0 otherwise
6 * @returns The index of the champion team
7 */
8function findChampion(grid: number[][]): number {
9    const n: number = grid.length;
10  
11    // Iterate through each team to check if it's the champion
12    for (let teamIndex: number = 0; teamIndex < n; teamIndex++) {
13        let winsCount: number = 0;
14      
15        // Count how many teams the current team beats
16        for (let opponentIndex: number = 0; opponentIndex < n; opponentIndex++) {
17            // Skip when comparing a team with itself
18            if (teamIndex !== opponentIndex && grid[teamIndex][opponentIndex] === 1) {
19                winsCount++;
20            }
21        }
22      
23        // A champion must beat all other teams (n-1 teams)
24        if (winsCount === n - 1) {
25            return teamIndex;
26        }
27    }
28  
29    // This line should never be reached if the input guarantees a champion exists
30    return -1;
31}
32

Time and Space Complexity

The time complexity is O(n^2), where n is the number of teams (rows/columns in the grid). This is because:

  • The outer loop iterates through each row, which runs n times
  • For each row, the all() function with the generator expression checks up to n-1 elements (all elements except when i == j)
  • In the worst case, we check all n rows before finding the champion (or the champion is the last row)
  • Therefore, the total operations are n × (n-1), which simplifies to O(n^2)

The space complexity is O(1). The algorithm only uses:

  • A few variables (i, row, j, x) for iteration
  • The generator expression inside all() doesn't create a list but yields values one at a time
  • No additional data structures are created that scale with input size
  • The space usage remains constant regardless of the grid size

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

Common Pitfalls

1. Misunderstanding the Grid Representation

A common mistake is misinterpreting what grid[i][j] represents. Some might think that both grid[i][j] = 1 AND grid[j][i] = 1 could be true simultaneously, or that the grid is symmetric. However, the problem clearly states that for any two teams, exactly one is stronger than the other.

Key Understanding:

  • grid[i][j] = 1 means team i beat team j
  • grid[i][j] = 0 means team j beat team i
  • For any i != j, if grid[i][j] = 1, then grid[j][i] = 0 (and vice versa)

2. Forgetting to Skip the Diagonal

The diagonal elements grid[i][i] represent a team playing against itself, which is undefined. A pitfall is including these elements in the champion check, which could lead to incorrect results.

Incorrect Implementation:

# Wrong: includes diagonal element
for i, row in enumerate(grid):
    if all(x == 1 for x in row):  # This checks grid[i][i] too!
        return i

Correct Implementation:

# Correct: excludes diagonal element
for i, row in enumerate(grid):
    if all(x == 1 for j, x in enumerate(row) if i != j):
        return i

3. Assuming Multiple Champions or No Champion

The problem guarantees that there will be exactly one champion. Some solutions might unnecessarily handle cases for:

  • No champion existing (returning -1 or None)
  • Multiple champions existing (returning a list)

These edge cases don't need to be handled based on the problem constraints, which simplifies the solution.

4. Inefficient Alternative Approaches

Some might try to count wins for each team and find the maximum, which is less efficient:

# Less efficient approach
win_counts = []
for i in range(len(grid)):
    wins = sum(grid[i][j] for j in range(len(grid)) if i != j)
    win_counts.append(wins)
return win_counts.index(max(win_counts))

While this works, it processes the entire grid even after finding a champion, whereas the original solution can return early once the champion is found.

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