LeetCode 1729 - Find Followers Count
The problem is asking us to determine, for each user in a social media application, how many followers they have. The input is a table called Followers with two columns: userid and followerid. Each row represents a relationship where followerid follows userid.
Difficulty: 🟢 Easy
Topics: Database
Solution
Problem Understanding
The problem is asking us to determine, for each user in a social media application, how many followers they have. The input is a table called Followers with two columns: user_id and follower_id. Each row represents a relationship where follower_id follows user_id. The combination (user_id, follower_id) is unique, so there are no duplicate follower entries for a user.
The expected output is a table containing each user_id along with the number of followers they have, labeled as followers_count. The results must be sorted by user_id in ascending order.
The constraints imply that the Followers table may contain multiple users and multiple followers per user, but every pair is unique. Edge cases include users with zero followers, a single user, or a single follower. The problem guarantees that user_id and follower_id are integers and the primary key ensures no duplicates.
Approaches
A brute-force approach would iterate over each user and count how many times that user appears as user_id in the table. While correct, this approach is inefficient because it may require scanning the entire table for every user, resulting in a high time complexity if the table is large.
The key insight for an optimal solution is to leverage SQL aggregation functions. By grouping the table by user_id and counting the rows for each group, we can calculate the number of followers directly and efficiently. Using GROUP BY ensures we process each user_id only once and ordering the result ensures it meets the output specification.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n * m) | O(u) | Count followers by scanning the table for each user; n is number of users, m is number of rows, u is distinct users |
| Optimal | O(m) | O(u) | Use SQL GROUP BY with COUNT(*); m is number of rows, u is distinct users |
Algorithm Walkthrough
- Identify all distinct
user_idvalues in theFollowerstable. - Group all rows by
user_id. This step ensures all followers for a specific user are collected together. - Count the number of rows in each group. This count represents the number of followers for that
user_id. - Construct the output table with columns
user_idandfollowers_count, using the counts obtained. - Order the results by
user_idin ascending order. This is necessary to satisfy the output specification.
Why it works: Each row in the Followers table corresponds to exactly one follower relationship. Grouping by user_id and counting the rows in each group directly yields the number of followers per user. Ordering ensures the results are presented as requested.
Python Solution
import sqlite3
from typing import List, Tuple
def find_followers_count(cursor: sqlite3.Cursor) -> List[Tuple[int, int]]:
query = """
SELECT user_id, COUNT(*) AS followers_count
FROM Followers
GROUP BY user_id
ORDER BY user_id ASC
"""
cursor.execute(query)
return cursor.fetchall()
In this implementation, the SQL query selects user_id and counts the number of followers for each user using COUNT(*). GROUP BY user_id groups the rows by user, and ORDER BY user_id ASC sorts the results. cursor.fetchall() returns the final list of tuples.
Go Solution
package main
import (
"database/sql"
_ "github.com/mattn/go-sqlite3"
)
func FindFollowersCount(db *sql.DB) ([][2]int, error) {
query := `
SELECT user_id, COUNT(*) AS followers_count
FROM Followers
GROUP BY user_id
ORDER BY user_id ASC;
`
rows, err := db.Query(query)
if err != nil {
return nil, err
}
defer rows.Close()
var results [][2]int
for rows.Next() {
var userID int
var count int
err := rows.Scan(&userID, &count)
if err != nil {
return nil, err
}
results = append(results, [2]int{userID, count})
}
return results, nil
}
In Go, we use db.Query to execute the SQL. We iterate over rows and scan the results into integer variables. We append each [2]int slice to the results. Go requires explicit error handling and closing of rows, which is handled with defer.
Worked Examples
For the given example:
Followers table:
| user_id | follower_id |
| 0 | 1 |
| 1 | 0 |
| 2 | 0 |
| 2 | 1 |
Step by step:
- Group by
user_id:
- 0 → [1] (count 1)
- 1 → [0] (count 1)
- 2 → [0, 1] (count 2)
- Build the result table:
| user_id | followers_count |
| 0 | 1 |
| 1 | 1 |
| 2 | 2 |
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(m) | Each row in the table is processed once in the GROUP BY operation, where m is the number of rows. |
| Space | O(u) | One entry per distinct user is stored for the aggregation, where u is the number of users. |
The reasoning: SQL aggregation functions are optimized to scan each row once while maintaining an internal count per group. This results in linear time with respect to the table size and linear space relative to the number of unique users.
Test Cases
# Test cases
import sqlite3
conn = sqlite3.connect(":memory:")
c = conn.cursor()
c.execute("CREATE TABLE Followers (user_id INT, follower_id INT)")
# Example 1
c.executemany("INSERT INTO Followers VALUES (?,?)", [(0,1),(1,0),(2,0),(2,1)])
assert find_followers_count(c) == [(0,1),(1,1),(2,2)] # standard case
# No followers
c.execute("DELETE FROM Followers")
c.executemany("INSERT INTO Followers VALUES (?,?)", [])
assert find_followers_count(c) == [] # empty table
# Single user multiple followers
c.execute("DELETE FROM Followers")
c.executemany("INSERT INTO Followers VALUES (?,?)", [(0,1),(0,2),(0,3)])
assert find_followers_count(c) == [(0,3)] # one user with many followers
# Multiple users, single follower each
c.execute("DELETE FROM Followers")
c.executemany("INSERT INTO Followers VALUES (?,?)", [(0,1),(1,2),(2,3)])
assert find_followers_count(c) == [(0,1),(1,1),(2,1)] # each user has one follower
| Test | Why |
|---|---|
| Standard example | Validates basic functionality and aggregation |
| Empty table | Checks handling of zero rows |
| Single user multiple followers | Ensures counting works with multiple followers for one user |
| Multiple users, single follower | Ensures grouping works when all users have exactly one follower |
Edge Cases
One important edge case is an empty Followers table. The function should return an empty list without errors, which our SQL handles gracefully as GROUP BY over zero rows results in zero output.
A second edge case is a user with no followers. Since the Followers table only contains existing relationships, users with zero followers will not appear in the table, and our function will not include them in the output, which matches the problem specification.
A third edge case is the presence of a single user followed by multiple users or having a single follower. Our implementation handles this by aggregating all rows for the given user_id and correctly counting the number of followers, regardless of how many exist. This ensures that both high and low fan-out scenarios are accounted for accurately.