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.
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 FCAhly 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:
- Pick a team as the home team.
- Iterate through all teams again as potential away teams.
- Skip the case where both teams are the same.
- 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
- Start with the
Teamstable and create two logical copies of it using aliases such ast1andt2.
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:
t1represents the home team.t2represents 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 n² 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.