Facebook Pixel

2924. Find Champion II

Problem Description

You have n teams in a tournament, numbered from 0 to n - 1. These teams form a Directed Acyclic Graph (DAG) where edges represent strength relationships between teams.

Given:

  • An integer n representing the number of teams
  • A 2D array edges where each edges[i] = [ui, vi] means team ui is stronger than team vi (there's a directed edge from ui to vi)

In this tournament:

  • If there's a directed edge from team a to team b, it means team a is stronger than team b
  • A team becomes the champion if no other team is stronger than it

Your task is to find the champion team. Return the team number if there's a unique champion, otherwise return -1.

The solution uses the concept of in-degrees in a directed graph. The in-degree of a node represents how many edges point to it. In this context:

  • If a team has in-degree 0, no team is stronger than it
  • If exactly one team has in-degree 0, that team is the unique champion
  • If multiple teams have in-degree 0 or no team has in-degree 0, there's no unique champion

The algorithm:

  1. Create an array indeg to count in-degrees for each team
  2. For each edge [u, v], increment the in-degree of team v (since u is stronger than v)
  3. Check if exactly one team has in-degree 0
  4. If yes, return that team's index; otherwise return -1
Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight comes from understanding what it means to be a champion in this tournament structure. A champion is a team that no other team is stronger than. In graph terms, this translates to finding a node that has no incoming edges.

Think about it this way: if a team has an incoming edge, it means there's at least one team stronger than it, so it cannot be the champion. Conversely, a team with no incoming edges has no team stronger than it, making it a potential champion.

Since we're working with a DAG (no cycles), we're guaranteed that at least one node will have no incoming edges. However, the problem asks for a unique champion. This means we need exactly one team with zero in-degree.

Why does counting in-degrees work?

  • Each edge [u, v] represents "u beats v"
  • The in-degree of a node tells us how many teams can beat it
  • A team with in-degree 0 cannot be beaten by any other team
  • If only one such team exists, it must be the unique champion

The beauty of this approach is its simplicity: we don't need to traverse the graph or perform complex operations. We just count how many times each team appears as the "loser" (the second element in each edge). The team that never appears as a loser, if unique, is our champion.

This leads directly to the solution: count in-degrees, then check if exactly one team has in-degree 0.

Learn more about Graph patterns.

Solution Approach

The implementation follows a straightforward approach using in-degree counting:

Step 1: Initialize the in-degree array

indeg = [0] * n

We create an array indeg of size n where indeg[i] will store the in-degree of team i. Initially, all teams have in-degree 0.

Step 2: Count in-degrees

for _, v in edges:
    indeg[v] += 1

We iterate through each edge in the edges array. For each edge [u, v]:

  • We ignore u (the stronger team) since we only care about counting incoming edges
  • We increment indeg[v] by 1 because team v has one more team stronger than it

Step 3: Check for unique champion

return -1 if indeg.count(0) != 1 else indeg.index(0)

This line does two things:

  • indeg.count(0) counts how many teams have in-degree 0 (potential champions)
  • If exactly one team has in-degree 0, we return its index using indeg.index(0)
  • If the count is not 1 (either 0 or more than 1), we return -1 indicating no unique champion

Time Complexity: O(m + n) where m is the number of edges and n is the number of teams

  • O(m) to iterate through all edges
  • O(n) to count zeros and find the index

Space Complexity: O(n) for storing the in-degree array

The elegance of this solution lies in its simplicity - by tracking just the in-degrees, we can determine the tournament champion without needing to build or traverse the actual graph structure.

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 and the following edges:

  • n = 4
  • edges = [[0,2], [1,3], [1,2]]

This means:

  • Team 0 is stronger than Team 2
  • Team 1 is stronger than Team 3
  • Team 1 is stronger than Team 2

Step 1: Initialize in-degree array

indeg = [0, 0, 0, 0]

All teams start with in-degree 0.

Step 2: Process each edge to count in-degrees

Processing edge [0,2]:

  • Team 0 beats Team 2, so increment indeg[2]
  • indeg = [0, 0, 1, 0]

Processing edge [1,3]:

  • Team 1 beats Team 3, so increment indeg[3]
  • indeg = [0, 0, 1, 1]

Processing edge [1,2]:

  • Team 1 beats Team 2, so increment indeg[2]
  • indeg = [0, 0, 2, 1]

Step 3: Find the champion

Final in-degree array: [0, 0, 2, 1]

Let's analyze what this means:

  • Team 0: in-degree = 0 (no team is stronger)
  • Team 1: in-degree = 0 (no team is stronger)
  • Team 2: in-degree = 2 (Teams 0 and 1 are stronger)
  • Team 3: in-degree = 1 (Team 1 is stronger)

Count teams with in-degree 0: There are 2 teams (Team 0 and Team 1)

Since we have more than one team with in-degree 0, there is no unique champion. The function returns -1.

Alternative Example with Unique Champion:

Let's modify the edges to [[0,2], [1,3], [1,2], [0,1]]:

Processing all edges:

  • [0,2]: indeg = [0, 0, 1, 0]
  • [1,3]: indeg = [0, 0, 1, 1]
  • [1,2]: indeg = [0, 0, 2, 1]
  • [0,1]: indeg = [0, 1, 2, 1]

Final: indeg = [0, 1, 2, 1]

Only Team 0 has in-degree 0, making it the unique champion. The function returns 0.

Solution Implementation

1class Solution:
2    def findChampion(self, n: int, edges: List[List[int]]) -> int:
3        # Initialize in-degree array to track incoming edges for each node
4        in_degree = [0] * n
5      
6        # Calculate in-degree for each node
7        # For each edge (u, v), increment the in-degree of node v
8        for _, destination in edges:
9            in_degree[destination] += 1
10      
11        # Check if there is exactly one node with in-degree 0 (potential champion)
12        # A champion must have no incoming edges (no one beats them)
13        if in_degree.count(0) != 1:
14            # Multiple or no champions found, return -1
15            return -1
16        else:
17            # Return the index of the unique node with in-degree 0
18            return in_degree.index(0)
19
1class Solution {
2    public int findChampion(int n, int[][] edges) {
3        // Create an array to store the in-degree of each node
4        // In-degree represents the number of incoming edges to a node
5        int[] inDegree = new int[n];
6      
7        // Calculate in-degree for each node by iterating through all edges
8        // For each edge from node u to node v, increment the in-degree of v
9        for (int[] edge : edges) {
10            inDegree[edge[1]]++;
11        }
12      
13        // Initialize variables to track the potential champion and count of nodes with zero in-degree
14        int champion = -1;
15        int zeroInDegreeCount = 0;
16      
17        // Iterate through all nodes to find nodes with zero in-degree
18        // A node with zero in-degree has no incoming edges (no one beats them)
19        for (int i = 0; i < n; i++) {
20            if (inDegree[i] == 0) {
21                zeroInDegreeCount++;
22                champion = i;
23            }
24        }
25      
26        // Return the champion if there's exactly one node with zero in-degree
27        // Otherwise return -1 (no unique champion exists)
28        return zeroInDegreeCount == 1 ? champion : -1;
29    }
30}
31
1class Solution {
2public:
3    int findChampion(int n, vector<vector<int>>& edges) {
4        // Initialize array to track in-degree for each node
5        // In-degree represents the number of incoming edges to a node
6        int inDegree[n];
7        memset(inDegree, 0, sizeof(inDegree));
8      
9        // Calculate in-degree for each node
10        // edges[i] = [from, to] represents a directed edge from 'from' to 'to'
11        for (auto& edge : edges) {
12            ++inDegree[edge[1]];  // Increment in-degree of destination node
13        }
14      
15        // Find nodes with zero in-degree (potential champions)
16        // A champion has no incoming edges (no one beats them)
17        int champion = -1;
18        int championCount = 0;
19      
20        for (int i = 0; i < n; ++i) {
21            if (inDegree[i] == 0) {
22                ++championCount;
23                champion = i;  // Store the potential champion node
24            }
25        }
26      
27        // Return the champion if exactly one exists, otherwise return -1
28        // Valid tournament must have exactly one champion
29        return championCount == 1 ? champion : -1;
30    }
31};
32
1/**
2 * Finds the champion node in a directed graph.
3 * A champion is a node with no incoming edges (indegree = 0).
4 * Returns the champion if there's exactly one, otherwise returns -1.
5 * 
6 * @param n - The number of nodes in the graph (0 to n-1)
7 * @param edges - Array of directed edges where each edge is [from, to]
8 * @returns The champion node index if unique, otherwise -1
9 */
10function findChampion(n: number, edges: number[][]): number {
11    // Initialize indegree array to track incoming edges for each node
12    const indegreeArray: number[] = Array(n).fill(0);
13  
14    // Calculate indegree for each node by iterating through all edges
15    for (const [fromNode, toNode] of edges) {
16        indegreeArray[toNode]++;
17    }
18  
19    // Track potential champion and count of nodes with zero indegree
20    let championNode: number = -1;
21    let zeroIndegreeCount: number = 0;
22  
23    // Find all nodes with zero indegree
24    for (let nodeIndex = 0; nodeIndex < n; nodeIndex++) {
25        if (indegreeArray[nodeIndex] === 0) {
26            zeroIndegreeCount++;
27            championNode = nodeIndex;
28        }
29    }
30  
31    // Return champion only if there's exactly one node with zero indegree
32    return zeroIndegreeCount === 1 ? championNode : -1;
33}
34

Time and Space Complexity

The time complexity is O(n + m), where n is the number of nodes and m is the number of edges. This breaks down as follows:

  • Initializing the indeg array takes O(n) time
  • Iterating through all edges to update in-degrees takes O(m) time
  • The count(0) operation scans through the entire indeg array, taking O(n) time
  • The index(0) operation in the worst case also takes O(n) time
  • Overall: O(n) + O(m) + O(n) + O(n) = O(n + m)

Since the problem typically represents a tournament or directed acyclic graph where m can be at most O(nΒ²) but is often O(n) in practice, and considering the reference answer simplifies to O(n), this suggests the edges are bounded by O(n) in the expected case.

The space complexity is O(n), as we create an indeg array of size n to store the in-degree of each node.

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

Common Pitfalls

1. Misunderstanding the Direction of Edges

A frequent mistake is confusing which team is stronger when processing edges. Given an edge [u, v], it means team u is stronger than team v (not the other way around). This leads to incorrectly incrementing the in-degree.

Incorrect Implementation:

for u, _ in edges:  # Wrong! This increments the stronger team's in-degree
    in_degree[u] += 1

Correct Implementation:

for _, v in edges:  # Correct! Increment the weaker team's in-degree
    in_degree[v] += 1

2. Not Handling Edge Cases Properly

The problem might have special cases that need careful consideration:

  • Empty graph with no edges: When edges = [] and n > 1, all teams have in-degree 0, so there's no unique champion
  • Single team: When n = 1, that team is automatically the champion regardless of edges

Enhanced Solution with Edge Case Handling:

def findChampion(self, n: int, edges: List[List[int]]) -> int:
    # Special case: single team is always champion
    if n == 1:
        return 0
  
    in_degree = [0] * n
    for _, v in edges:
        in_degree[v] += 1
  
    # Find all teams with in-degree 0
    champions = [i for i in range(n) if in_degree[i] == 0]
  
    # Return champion only if exactly one exists
    return champions[0] if len(champions) == 1 else -1

3. Inefficient Champion Detection

Using count() and index() separately requires two passes through the array, which can be optimized.

Less Efficient:

if in_degree.count(0) != 1:
    return -1
else:
    return in_degree.index(0)

More Efficient Single-Pass Solution:

champion = -1
champion_count = 0

for i in range(n):
    if in_degree[i] == 0:
        champion = i
        champion_count += 1
        if champion_count > 1:  # Early exit if multiple champions found
            return -1

return champion if champion_count == 1 else -1

4. Assuming Graph Connectivity

Don't assume the graph is connected. It's possible to have isolated components or teams that neither beat nor are beaten by certain other teams. The in-degree approach handles this correctly, but it's important to understand that a champion with in-degree 0 might not be able to beat all other teams directly - it just means no team can beat them.

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

What's the output of running the following function using the following tree as input?

1def serialize(root):
2    res = []
3    def dfs(root):
4        if not root:
5            res.append('x')
6            return
7        res.append(root.val)
8        dfs(root.left)
9        dfs(root.right)
10    dfs(root)
11    return ' '.join(res)
12
1import java.util.StringJoiner;
2
3public static String serialize(Node root) {
4    StringJoiner res = new StringJoiner(" ");
5    serializeDFS(root, res);
6    return res.toString();
7}
8
9private static void serializeDFS(Node root, StringJoiner result) {
10    if (root == null) {
11        result.add("x");
12        return;
13    }
14    result.add(Integer.toString(root.val));
15    serializeDFS(root.left, result);
16    serializeDFS(root.right, result);
17}
18
1function serialize(root) {
2    let res = [];
3    serialize_dfs(root, res);
4    return res.join(" ");
5}
6
7function serialize_dfs(root, res) {
8    if (!root) {
9        res.push("x");
10        return;
11    }
12    res.push(root.val);
13    serialize_dfs(root.left, res);
14    serialize_dfs(root.right, res);
15}
16

Recommended Readings

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

Load More