LeetCode 2339 - All the Matches of the League

The Teams table contains the names of all teams participating in a league. Each row represents exactly one team, and the teamname column is guaranteed to contain unique values.

LeetCode Problem 2339

Difficulty: 🟢 Easy
Topics: Database

Solution

Problem Understanding

The Teams table contains the names of all teams participating in a league. Each row represents exactly one team, and the team_name column is guaranteed to contain unique values.

The problem asks us to generate every possible match in the league under the following rule:

  • Every pair of teams plays twice.
  • In one match, Team A is the home team and Team B is the away team.
  • In the other match, Team B becomes the home team and Team A becomes the away team.

This means we must generate all ordered pairs of distinct teams.

For example, if we have:

  • Leetcode FC
  • Ahly SC

Then the output must contain both:

  • (Leetcode FC, Ahly SC)
  • (Ahly SC, Leetcode FC)

The important detail is that a team cannot play against itself. Therefore, combinations like:

  • (Leetcode FC, Leetcode FC)

must never appear in the result.

The input is a single table:

Column Meaning
team_name Name of a team

The expected output contains:

Column Meaning
home_team Team playing at home
away_team Team playing away

The problem statement allows the result to be returned in any order, so we do not need to apply any sorting unless explicitly required.

Since this is a database problem, the main challenge is expressing the relationship between every team and every other team using SQL operations. The natural operation for this type of problem is a Cartesian product, also known as a cross join.

An important observation is that the input guarantees unique team names. This simplifies the logic because we do not need to worry about duplicate teams generating repeated matches.

The main edge case is when the table contains only one team. In that case, no valid matches exist because a team cannot play itself, so the result should be empty.

Approaches

Brute Force Approach

The brute force idea is to compare every team against every other team manually.

Conceptually, we can think of this as two nested loops:

  1. Pick a team as the home team.
  2. Iterate through all teams again as potential away teams.
  3. Skip the case where both teams are the same.
  4. Output the remaining pairs.

This approach is guaranteed to produce all valid matches because every possible ordered pair of distinct teams is checked exactly once.

In pseudocode, the logic looks like this:

for each team A:
    for each team B:
        if A != B:
            output (A, B)

Although this approach is straightforward, implementing nested iteration manually is not how SQL is typically optimized. In SQL, a Cartesian product operation already performs this efficiently and declaratively.

Optimal Approach

The key observation is that we need all ordered pairs of different teams. This is exactly what a CROSS JOIN produces.

A CROSS JOIN combines every row from the first table with every row from the second table. Since we are joining the Teams table with itself, we generate every possible pair of teams.

However, this also includes cases where a team is paired with itself. We remove those invalid matches using a WHERE clause:

WHERE t1.team_name != t2.team_name

This gives us every valid home-away pairing.

This solution is concise, efficient, and directly models the problem requirements.

Approach Time Complexity Space Complexity Notes
Brute Force O(n²) O(1) excluding output Manually compares every pair of teams
Optimal O(n²) O(1) excluding output Uses SQL CROSS JOIN to generate all ordered pairs

Algorithm Walkthrough

  1. Start with the Teams table and create two logical copies of it using aliases such as t1 and t2.

We need two references because one represents the home team and the other represents the away team. 2. Perform a CROSS JOIN between t1 and t2.

A cross join creates every possible pairing between rows in the two tables. If there are n teams, this produces n × n combinations. 3. Remove invalid matches where a team plays against itself.

We apply the condition:

t1.team_name != t2.team_name

This filters out self matches such as (Real Madrid, Real Madrid). 4. Select the columns with appropriate names.

The first table alias becomes home_team, and the second becomes away_team. 5. Return the result.

Since the problem allows any order, no sorting is necessary.

Why it works

The algorithm works because a CROSS JOIN generates every ordered pair of teams. The filtering condition removes exactly the invalid self-pairings and keeps all distinct team combinations. Since every valid pair appears once in each direction, the output correctly represents all league matches.

Python Solution

Even though this is fundamentally a SQL problem, LeetCode database solutions are usually written directly in SQL. Below is the complete SQL solution embedded inside a Python string for presentation purposes.

class Solution:
    def allMatches(self):
        return """
        SELECT
            t1.team_name AS home_team,
            t2.team_name AS away_team
        FROM Teams t1
        CROSS JOIN Teams t2
        WHERE t1.team_name != t2.team_name
        """

The solution uses two aliases of the same table:

  • t1 represents the home team.
  • t2 represents the away team.

The CROSS JOIN generates every possible pair. The WHERE clause removes self matches, ensuring that only valid league games remain.

Because ordered pairs are preserved, both:

  • (A, B)
  • (B, A)

appear in the output.

Go Solution

LeetCode database problems do not require Go implementations because the execution environment expects SQL queries. However, to mirror the logic algorithmically, the equivalent Go implementation would look like this.

package main

import "fmt"

type Match struct {
	HomeTeam string
	AwayTeam string
}

func allMatches(teams []string) []Match {
	var matches []Match

	for i := 0; i < len(teams); i++ {
		for j := 0; j < len(teams); j++ {
			if i != j {
				matches = append(matches, Match{
					HomeTeam: teams[i],
					AwayTeam: teams[j],
				})
			}
		}
	}

	return matches
}

func main() {
	teams := []string{
		"Leetcode FC",
		"Ahly SC",
		"Real Madrid",
	}

	result := allMatches(teams)

	for _, match := range result {
		fmt.Println(match.HomeTeam, "vs", match.AwayTeam)
	}
}

The Go version explicitly uses nested loops to simulate the Cartesian product that SQL performs automatically with a CROSS JOIN.

Unlike Python or SQL, Go requires a custom struct to represent each match. The logic remains identical:

  • Iterate through every pair of teams.
  • Skip identical indices.
  • Store valid matches in a slice.

Worked Examples

Example 1

Input table:

team_name
Leetcode FC
Ahly SC
Real Madrid

Step 1: CROSS JOIN Generation

The cross join produces every possible pairing.

t1.team_name t2.team_name
Leetcode FC Leetcode FC
Leetcode FC Ahly SC
Leetcode FC Real Madrid
Ahly SC Leetcode FC
Ahly SC Ahly SC
Ahly SC Real Madrid
Real Madrid Leetcode FC
Real Madrid Ahly SC
Real Madrid Real Madrid

There are 3 × 3 = 9 total combinations.

Step 2: Remove Self Matches

We remove rows where both team names are identical.

Filtered result:

home_team away_team
Leetcode FC Ahly SC
Leetcode FC Real Madrid
Ahly SC Leetcode FC
Ahly SC Real Madrid
Real Madrid Leetcode FC
Real Madrid Ahly SC

Now we have exactly all valid league matches.

Complexity Analysis

Measure Complexity Explanation
Time O(n²) Every team is paired with every other team
Space O(1) excluding output SQL uses constant auxiliary space

The dominant operation is the Cartesian product between the two copies of the Teams table. If there are n teams, then pairs are generated before filtering.

The output itself also contains n × (n - 1) rows, so quadratic growth is unavoidable because every valid match must be returned.

Test Cases

def generate_matches(teams):
    result = []

    for home in teams:
        for away in teams:
            if home != away:
                result.append((home, away))

    return result

# Example case from problem statement
assert sorted(generate_matches(
    ["Leetcode FC", "Ahly SC", "Real Madrid"]
)) == sorted([
    ("Leetcode FC", "Ahly SC"),
    ("Leetcode FC", "Real Madrid"),
    ("Ahly SC", "Leetcode FC"),
    ("Ahly SC", "Real Madrid"),
    ("Real Madrid", "Leetcode FC"),
    ("Real Madrid", "Ahly SC"),
])  # Standard three-team example

# Single team, no matches possible
assert generate_matches(["Barcelona"]) == []  # Edge case with one team

# Two teams should generate exactly two matches
assert sorted(generate_matches(
    ["A", "B"]
)) == sorted([
    ("A", "B"),
    ("B", "A"),
])  # Smallest valid league

# Four teams, verify total match count
result = generate_matches(["A", "B", "C", "D"])
assert len(result) == 12  # 4 * (4 - 1)

# Ensure no self matches appear
result = generate_matches(["X", "Y", "Z"])
assert all(home != away for home, away in result)  # No team plays itself

# Verify ordering direction matters
result = generate_matches(["A", "B"])
assert ("A", "B") in result
assert ("B", "A") in result  # Both directions required
Test Why
Three-team example Validates the standard case from the problem
Single team Ensures empty output when no opponents exist
Two teams Verifies both directional matches are produced
Four teams count Confirms quadratic number of matches
No self matches Ensures filtering logic works correctly
Directional validation Confirms (A, B) and (B, A) are treated separately

Edge Cases

Single Team

If the table contains only one team, the cross join still produces one row:

(A, A)

However, the filtering condition removes it because a team cannot play against itself. The final result is empty. A naive implementation might accidentally include self matches if the filtering step is forgotten.

Two Teams

With exactly two teams, there should be precisely two matches:

(A, B)
(B, A)

Some incorrect implementations mistakenly generate only one combination by treating the relationship as unordered. The problem explicitly requires both home-away directions.

Self Matches

The most common bug is forgetting to exclude matches where the home and away teams are identical. Since a cross join naturally creates these combinations, the condition:

WHERE t1.team_name != t2.team_name

is essential for correctness.

Duplicate Team Names

The problem guarantees that team_name values are unique. This simplifies the logic because duplicate rows could otherwise generate repeated matches. Since uniqueness is guaranteed, each generated pair corresponds to exactly one valid match.