Facebook Pixel

1500. Design a File Sharing System 🔒

Problem Description

This problem asks you to design a file-sharing system that manages users and file chunks. The system needs to handle three main operations:

System Overview:

  • A large file is divided into m chunks, numbered from 1 to m
  • Users can join the system with certain chunks they already own
  • Users can request chunks from other users
  • Users can leave the system

Key Requirements:

  1. User ID Management:

    • When a user joins, assign them the smallest available positive integer ID
    • IDs start from 1 and increment
    • When a user leaves, their ID becomes available for reuse
    • Reused IDs should be assigned before creating new higher IDs
  2. Operations to Implement:

    • FileSharing(m): Initialize the system with a file containing m chunks
    • join(ownedChunks): Register a new user who owns the specified chunks, return their assigned ID
    • leave(userID): Remove a user from the system, making their ID available for reuse
    • request(userID, chunkID): User requests a specific chunk
      • Returns a sorted list of all user IDs who own that chunk
      • If the list is non-empty (meaning the chunk was found), the requesting user also receives the chunk

Example Walkthrough:

  • User joins with chunks [1,2] → gets ID 1
  • User joins with chunks [2,3] → gets ID 2
  • User joins with chunk [4] → gets ID 3
  • User 1 requests chunk 3 → gets [2] (only user 2 has it), now user 1 has chunks [1,2,3]
  • User 2 requests chunk 2 → gets [1,2] (both users have it)
  • User 1 leaves → ID 1 becomes available
  • User 2 requests chunk 1 → gets [] (no one has it after user 1 left)
  • New user joins → gets ID 1 (reused)

The solution uses a min-heap to efficiently track and reuse the smallest available user IDs, and a dictionary to map each user to their set of owned chunks. When requesting a chunk, it searches through all users to find owners and automatically adds the chunk to the requester if found.

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

Intuition

The key insight is that we need to efficiently manage two things: user IDs and chunk ownership. Let's think through each requirement:

For User ID Assignment: We need to always assign the smallest available ID. Initially, this seems simple - just use a counter starting at 1. But the challenge comes when users leave and we need to reuse their IDs. If users 1, 2, 3 exist and user 2 leaves, the next user should get ID 2, not ID 4.

This naturally leads us to use a min-heap to track available IDs. When a user leaves, we push their ID into the heap. When a new user joins, we first check if there are any reusable IDs in the heap (take the smallest one), otherwise increment our counter for a new ID.

For Chunk Ownership Tracking: We need to quickly answer "which users own chunk X?" and also track what chunks each user owns. A dictionary mapping user IDs to sets of chunks works perfectly here:

  • user_chunks[userID] gives us all chunks a user owns
  • To find who owns a specific chunk, we iterate through all users and check if the chunk is in their set

Using a set for each user's chunks makes sense because:

  • Checking if a user owns a chunk is O(1)
  • Adding a new chunk when a request succeeds is O(1)
  • The problem guarantees owned chunks are unique per user

For the Request Operation: When a user requests a chunk, we need to:

  1. Find all users who own it (iterate through our dictionary)
  2. If found, give the chunk to the requesting user
  3. Return the sorted list of owners

The sorting requirement at the end is straightforward - we collect all owner IDs in a list and sort before returning.

This approach balances simplicity with efficiency - we're not over-engineering with complex data structures, but we're using the right tools (heap for ID management, dictionary with sets for ownership) to make operations reasonably fast.

Learn more about Data Stream, Sorting and Heap (Priority Queue) patterns.

Solution Approach

Let's walk through the implementation step by step:

Data Structure Setup:

def __init__(self, m: int):
    self.cur = 0                    # Track the highest ID assigned so far
    self.chunks = m                 # Total number of chunks in the file
    self.reused = []                # Min-heap for reusable IDs
    self.user_chunks = defaultdict(set)  # Map userID -> set of owned chunks

We initialize four instance variables:

  • cur: Counter for the next new ID to assign (when no reused IDs are available)
  • chunks: Store the total number of chunks for validation
  • reused: Min-heap to efficiently get the smallest reusable ID
  • user_chunks: Dictionary mapping each user to their set of owned chunks

Join Operation:

def join(self, ownedChunks: List[int]) -> int:
    if self.reused:
        userID = heappop(self.reused)  # Reuse smallest available ID
    else:
        self.cur += 1
        userID = self.cur              # Assign new ID
    self.user_chunks[userID] = set(ownedChunks)
    return userID

The algorithm follows this logic:

  1. Check if there are any reusable IDs in the heap
  2. If yes, pop the smallest one (min-heap property guarantees this)
  3. If no, increment cur and use it as the new ID
  4. Store the user's chunks as a set in our dictionary
  5. Return the assigned ID

Leave Operation:

def leave(self, userID: int) -> None:
    heappush(self.reused, userID)      # Make ID available for reuse
    self.user_chunks.pop(userID)       # Remove user's chunk ownership

When a user leaves:

  1. Push their ID into the reused heap for future assignment
  2. Remove their entry from the user_chunks dictionary

Request Operation:

def request(self, userID: int, chunkID: int) -> List[int]:
    if chunkID < 1 or chunkID > self.chunks:
        return []
    res = []
    for k, v in self.user_chunks.items():
        if chunkID in v:
            res.append(k)
    if res:
        self.user_chunks[userID].add(chunkID)
    return sorted(res)

The request logic:

  1. First validate the chunk ID is within valid range [1, m]
  2. Iterate through all users in the system
  3. For each user, check if they own the requested chunk (O(1) set lookup)
  4. Collect all user IDs who own the chunk
  5. If at least one user owns it (non-empty result), add the chunk to the requester's set
  6. Return the sorted list of owner IDs

Time Complexity Analysis:

  • join: O(log k) where k is the number of reused IDs, due to heap operations
  • leave: O(log k) for heap push operation
  • request: O(n + n log n) where n is the number of users, for iterating through all users and sorting the result

Space Complexity:

  • O(n × c) where n is the number of users and c is the average number of chunks per user
  • Additional O(k) for the reused IDs heap

The solution elegantly handles ID reuse with a min-heap while maintaining efficient chunk ownership queries using sets, providing a good balance between implementation simplicity and performance.

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 trace through a small example to see how the solution works:

Initial Setup: FileSharing(4) - System initialized with 4 chunks

  • cur = 0, reused = [] (empty heap), user_chunks = {} (empty dict)

Step 1: User joins with chunks [1, 3]

  • Check reused heap - it's empty
  • Increment cur to 1, assign userID = 1
  • Store user_chunks[1] = {1, 3}
  • Return 1

State: cur = 1, reused = [], user_chunks = {1: {1, 3}}

Step 2: User joins with chunks [2]

  • Check reused heap - still empty
  • Increment cur to 2, assign userID = 2
  • Store user_chunks[2] = {2}
  • Return 2

State: cur = 2, reused = [], user_chunks = {1: {1, 3}, 2: {2}}

Step 3: User 1 requests chunk 2

  • Iterate through all users:
    • User 1: chunk 2 not in {1, 3}
    • User 2: chunk 2 IS in {2} ✓
  • Result list = [2]
  • Since result is non-empty, add chunk 2 to user 1: user_chunks[1] = {1, 2, 3}
  • Return sorted([2]) = [2]

State: user_chunks = {1: {1, 2, 3}, 2: {2}}

Step 4: User 2 leaves

  • Push userID 2 into reused heap: heappush(reused, 2)
  • Remove user 2: user_chunks.pop(2)

State: cur = 2, reused = [2], user_chunks = {1: {1, 2, 3}}

Step 5: New user joins with chunks [4]

  • Check reused heap - contains [2]
  • Pop smallest ID: userID = heappop(reused) = 2 (ID reused!)
  • Store user_chunks[2] = {4}
  • Return 2

State: cur = 2, reused = [], user_chunks = {1: {1, 2, 3}, 2: {4}}

Step 6: User 2 requests chunk 3

  • Iterate through all users:
    • User 1: chunk 3 IS in {1, 2, 3} ✓
    • User 2: chunk 3 not in {4}
  • Result list = [1]
  • Since result is non-empty, add chunk 3 to user 2: user_chunks[2] = {3, 4}
  • Return sorted([1]) = [1]

Final State: user_chunks = {1: {1, 2, 3}, 2: {3, 4}}

This example demonstrates:

  • How the min-heap ensures we reuse the smallest available ID (user 2's ID was reused)
  • How chunk ownership is tracked with sets for O(1) lookup
  • How successful requests automatically grant the chunk to the requester
  • How the system maintains sorted order in request results

Solution Implementation

1from collections import defaultdict
2from heapq import heappush, heappop
3from typing import List
4
5
6class FileSharing:
7    def __init__(self, m: int):
8        """
9        Initialize the file sharing system with m chunks.
10      
11        Args:
12            m: Total number of chunks in the file
13        """
14        self.current_max_user_id = 0  # Track the highest user ID assigned
15        self.total_chunks = m  # Total number of chunks in the file
16        self.reusable_user_ids = []  # Min heap to store reusable user IDs
17        self.user_to_chunks = defaultdict(set)  # Map user ID to their owned chunks
18
19    def join(self, ownedChunks: List[int]) -> int:
20        """
21        Add a new user to the system with their initially owned chunks.
22      
23        Args:
24            ownedChunks: List of chunk IDs that the user initially owns
25          
26        Returns:
27            The assigned user ID
28        """
29        # Assign user ID - reuse if available, otherwise increment
30        if self.reusable_user_ids:
31            user_id = heappop(self.reusable_user_ids)
32        else:
33            self.current_max_user_id += 1
34            user_id = self.current_max_user_id
35      
36        # Store the user's owned chunks
37        self.user_to_chunks[user_id] = set(ownedChunks)
38      
39        return user_id
40
41    def leave(self, userID: int) -> None:
42        """
43        Remove a user from the system and make their ID available for reuse.
44      
45        Args:
46            userID: The ID of the user leaving the system
47        """
48        # Add the user ID to the reusable pool
49        heappush(self.reusable_user_ids, userID)
50      
51        # Remove the user's chunk ownership data
52        self.user_to_chunks.pop(userID)
53
54    def request(self, userID: int, chunkID: int) -> List[int]:
55        """
56        Request a specific chunk for a user and return list of users who own it.
57        If any users own the chunk, the requesting user also receives it.
58      
59        Args:
60            userID: The ID of the user requesting the chunk
61            chunkID: The ID of the chunk being requested
62          
63        Returns:
64            Sorted list of user IDs who own the requested chunk
65        """
66        # Validate chunk ID is within valid range
67        if chunkID < 1 or chunkID > self.total_chunks:
68            return []
69      
70        # Find all users who own the requested chunk
71        users_with_chunk = []
72        for user_id, owned_chunks in self.user_to_chunks.items():
73            if chunkID in owned_chunks:
74                users_with_chunk.append(user_id)
75      
76        # If chunk is available, give it to the requesting user
77        if users_with_chunk:
78            self.user_to_chunks[userID].add(chunkID)
79      
80        # Return sorted list of users who had the chunk
81        return sorted(users_with_chunk)
82
83
84# Your FileSharing object will be instantiated and called as such:
85# obj = FileSharing(m)
86# param_1 = obj.join(ownedChunks)
87# obj.leave(userID)
88# param_3 = obj.request(userID, chunkID)
89
1class FileSharing {
2    // Total number of chunks in the file
3    private int totalChunks;
4  
5    // Counter for assigning new user IDs
6    private int currentUserId;
7  
8    // Set to store reusable user IDs (from users who left)
9    private TreeSet<Integer> reusableUserIds;
10  
11    // Map to store each user's owned chunks: userId -> set of chunk IDs
12    private TreeMap<Integer, Set<Integer>> userToChunksMap;
13
14    /**
15     * Initialize the file sharing system with m chunks
16     * @param m the total number of chunks in the file
17     */
18    public FileSharing(int m) {
19        currentUserId = 0;
20        totalChunks = m;
21        reusableUserIds = new TreeSet<>();
22        userToChunksMap = new TreeMap<>();
23    }
24
25    /**
26     * A new user joins the system with initially owned chunks
27     * @param ownedChunks list of chunk IDs the user initially owns
28     * @return the assigned user ID
29     */
30    public int join(List<Integer> ownedChunks) {
31        int userId;
32      
33        // Assign user ID: reuse an old ID if available, otherwise increment counter
34        if (reusableUserIds.isEmpty()) {
35            currentUserId++;
36            userId = currentUserId;
37        } else {
38            userId = reusableUserIds.pollFirst();
39        }
40      
41        // Store the user's owned chunks
42        userToChunksMap.put(userId, new HashSet<>(ownedChunks));
43      
44        return userId;
45    }
46
47    /**
48     * A user leaves the system
49     * @param userID the ID of the user leaving
50     */
51    public void leave(int userID) {
52        // Add the user ID back to reusable pool
53        reusableUserIds.add(userID);
54      
55        // Remove the user's chunk ownership data
56        userToChunksMap.remove(userID);
57    }
58
59    /**
60     * A user requests a specific chunk from the system
61     * @param userID the ID of the requesting user
62     * @param chunkID the ID of the requested chunk
63     * @return sorted list of user IDs who own the requested chunk
64     */
65    public List<Integer> request(int userID, int chunkID) {
66        // Validate chunk ID is within valid range
67        if (chunkID < 1 || chunkID > totalChunks) {
68            return Collections.emptyList();
69        }
70      
71        List<Integer> usersWithChunk = new ArrayList<>();
72      
73        // Find all users who own the requested chunk
74        for (Map.Entry<Integer, Set<Integer>> entry : userToChunksMap.entrySet()) {
75            if (entry.getValue().contains(chunkID)) {
76                usersWithChunk.add(entry.getKey());
77            }
78        }
79      
80        // If chunk is available, add it to the requesting user's owned chunks
81        if (!usersWithChunk.isEmpty()) {
82            userToChunksMap.computeIfAbsent(userID, k -> new HashSet<>()).add(chunkID);
83        }
84      
85        return usersWithChunk;
86    }
87}
88
89/**
90 * Your FileSharing object will be instantiated and called as such:
91 * FileSharing obj = new FileSharing(m);
92 * int param_1 = obj.join(ownedChunks);
93 * obj.leave(userID);
94 * List<Integer> param_3 = obj.request(userID,chunkID);
95 */
96
1class FileSharing {
2private:
3    // Total number of chunks in the file
4    int totalChunks;
5  
6    // Counter for assigning new user IDs
7    int currentUserId;
8  
9    // Set to store reusable user IDs (from users who left)
10    set<int> reusableUserIds;
11  
12    // Map to store each user's owned chunks: userId -> set of chunk IDs
13    map<int, set<int>> userToChunksMap;
14
15public:
16    /**
17     * Initialize the file sharing system with m chunks
18     * @param m the total number of chunks in the file
19     */
20    FileSharing(int m) {
21        currentUserId = 0;
22        totalChunks = m;
23    }
24  
25    /**
26     * A new user joins the system with initially owned chunks
27     * @param ownedChunks list of chunk IDs the user initially owns
28     * @return the assigned user ID
29     */
30    int join(vector<int>& ownedChunks) {
31        int userId;
32      
33        // Assign user ID: reuse an old ID if available, otherwise increment counter
34        if (reusableUserIds.empty()) {
35            currentUserId++;
36            userId = currentUserId;
37        } else {
38            userId = *reusableUserIds.begin();
39            reusableUserIds.erase(reusableUserIds.begin());
40        }
41      
42        // Store the user's owned chunks
43        userToChunksMap[userId] = set<int>(ownedChunks.begin(), ownedChunks.end());
44      
45        return userId;
46    }
47  
48    /**
49     * A user leaves the system
50     * @param userID the ID of the user leaving
51     */
52    void leave(int userID) {
53        // Add the user ID back to reusable pool
54        reusableUserIds.insert(userID);
55      
56        // Remove the user's chunk ownership data
57        userToChunksMap.erase(userID);
58    }
59  
60    /**
61     * A user requests a specific chunk from the system
62     * @param userID the ID of the requesting user
63     * @param chunkID the ID of the requested chunk
64     * @return sorted list of user IDs who own the requested chunk
65     */
66    vector<int> request(int userID, int chunkID) {
67        // Validate chunk ID is within valid range
68        if (chunkID < 1 || chunkID > totalChunks) {
69            return vector<int>();
70        }
71      
72        vector<int> usersWithChunk;
73      
74        // Find all users who own the requested chunk
75        for (const auto& entry : userToChunksMap) {
76            if (entry.second.count(chunkID) > 0) {
77                usersWithChunk.push_back(entry.first);
78            }
79        }
80      
81        // If chunk is available, add it to the requesting user's owned chunks
82        if (!usersWithChunk.empty()) {
83            userToChunksMap[userID].insert(chunkID);
84        }
85      
86        return usersWithChunk;
87    }
88};
89
90/**
91 * Your FileSharing object will be instantiated and called as such:
92 * FileSharing* obj = new FileSharing(m);
93 * int param_1 = obj->join(ownedChunks);
94 * obj->leave(userID);
95 * vector<int> param_3 = obj->request(userID,chunkID);
96 */
97
1// Total number of chunks in the file
2let totalChunks: number;
3
4// Counter for assigning new user IDs
5let currentUserId: number;
6
7// Set to store reusable user IDs (from users who left)
8let reusableUserIds: Set<number>;
9
10// Map to store each user's owned chunks: userId -> set of chunk IDs
11let userToChunksMap: Map<number, Set<number>>;
12
13/**
14 * Initialize the file sharing system with m chunks
15 * @param m - the total number of chunks in the file
16 */
17function FileSharing(m: number): void {
18    currentUserId = 0;
19    totalChunks = m;
20    reusableUserIds = new Set<number>();
21    userToChunksMap = new Map<number, Set<number>>();
22}
23
24/**
25 * A new user joins the system with initially owned chunks
26 * @param ownedChunks - list of chunk IDs the user initially owns
27 * @returns the assigned user ID
28 */
29function join(ownedChunks: number[]): number {
30    let userId: number;
31  
32    // Assign user ID: reuse an old ID if available, otherwise increment counter
33    if (reusableUserIds.size === 0) {
34        currentUserId++;
35        userId = currentUserId;
36    } else {
37        // Get the smallest reusable ID (TreeSet behavior)
38        userId = Math.min(...Array.from(reusableUserIds));
39        reusableUserIds.delete(userId);
40    }
41  
42    // Store the user's owned chunks
43    userToChunksMap.set(userId, new Set<number>(ownedChunks));
44  
45    return userId;
46}
47
48/**
49 * A user leaves the system
50 * @param userID - the ID of the user leaving
51 */
52function leave(userID: number): void {
53    // Add the user ID back to reusable pool
54    reusableUserIds.add(userID);
55  
56    // Remove the user's chunk ownership data
57    userToChunksMap.delete(userID);
58}
59
60/**
61 * A user requests a specific chunk from the system
62 * @param userID - the ID of the requesting user
63 * @param chunkID - the ID of the requested chunk
64 * @returns sorted list of user IDs who own the requested chunk
65 */
66function request(userID: number, chunkID: number): number[] {
67    // Validate chunk ID is within valid range
68    if (chunkID < 1 || chunkID > totalChunks) {
69        return [];
70    }
71  
72    const usersWithChunk: number[] = [];
73  
74    // Find all users who own the requested chunk
75    for (const [userId, chunks] of userToChunksMap.entries()) {
76        if (chunks.has(chunkID)) {
77            usersWithChunk.push(userId);
78        }
79    }
80  
81    // If chunk is available, add it to the requesting user's owned chunks
82    if (usersWithChunk.length > 0) {
83        if (!userToChunksMap.has(userID)) {
84            userToChunksMap.set(userID, new Set<number>());
85        }
86        userToChunksMap.get(userID)!.add(chunkID);
87    }
88  
89    // Sort the list before returning (TreeMap behavior ensures sorted order)
90    return usersWithChunk.sort((a, b) => a - b);
91}
92
93/**
94 * Your FileSharing object will be instantiated and called as such:
95 * FileSharing(m);
96 * let param_1 = join(ownedChunks);
97 * leave(userID);
98 * let param_3 = request(userID, chunkID);
99 */
100

Time and Space Complexity

Time Complexity:

  • __init__(m): O(1) - Simply initializes variables with constant time operations.
  • join(ownedChunks): O(k + log n) where k is the length of ownedChunks and n is the number of reused user IDs in the heap. The heap operation heappop takes O(log n) time, and creating a set from ownedChunks takes O(k) time.
  • leave(userID): O(log n) where n is the number of reused user IDs. The heappush operation takes O(log n) time, and dictionary pop is O(1).
  • request(userID, chunkID): O(u) where u is the total number of active users. We iterate through all users in user_chunks to find those who own the chunk, which takes O(u) time. The set membership check (chunkID in v) is O(1) for each user. Sorting the result takes O(u log u) in the worst case when all users have the chunk, but since we're iterating through all users anyway, the overall complexity is O(u log u).

Space Complexity:

  • Overall space: O(u * c) where u is the number of active users and c is the average number of chunks per user.
  • self.reused: O(n) where n is the number of left users (reused IDs in the heap).
  • self.user_chunks: O(u * c) where u is the number of active users and c is the average number of chunks each user owns.
  • The maximum possible space would be O(u * m) if all users own all chunks, where m is the total number of chunks.

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

Common Pitfalls

1. Not Handling Edge Cases in Request Method

A common mistake is forgetting to handle the case where the requesting user already owns the chunk they're requesting. This could lead to duplicate additions or incorrect return values.

Pitfall Example:

def request(self, userID: int, chunkID: int) -> List[int]:
    users_with_chunk = []
    for user_id, owned_chunks in self.user_to_chunks.items():
        if chunkID in owned_chunks:
            users_with_chunk.append(user_id)
  
    # Bug: Always adds chunk even if user already has it
    if users_with_chunk:
        self.user_to_chunks[userID].add(chunkID)  # Set handles duplicates, but logic is unclear
  
    return sorted(users_with_chunk)

Better Approach:

def request(self, userID: int, chunkID: int) -> List[int]:
    if chunkID < 1 or chunkID > self.total_chunks:
        return []
  
    users_with_chunk = []
    already_has_chunk = False
  
    for user_id, owned_chunks in self.user_to_chunks.items():
        if chunkID in owned_chunks:
            users_with_chunk.append(user_id)
            if user_id == userID:
                already_has_chunk = True
  
    # Only add if user doesn't already have it and someone else does
    if users_with_chunk and not already_has_chunk:
        self.user_to_chunks[userID].add(chunkID)
  
    return sorted(users_with_chunk)

2. Incorrect User ID Reuse Logic

Many implementations fail to properly manage the reusable ID pool, especially when dealing with consecutive leave/join operations.

Pitfall Example:

def join(self, ownedChunks: List[int]) -> int:
    # Bug: Not using a min-heap, just a list
    if self.reusable_user_ids:
        user_id = self.reusable_user_ids.pop()  # Gets last element, not smallest!
    else:
        self.current_max_user_id += 1
        user_id = self.current_max_user_id
  
    self.user_to_chunks[user_id] = set(ownedChunks)
    return user_id

Correct Implementation:

def join(self, ownedChunks: List[int]) -> int:
    # Properly use heappop to get smallest ID
    if self.reusable_user_ids:
        user_id = heappop(self.reusable_user_ids)
    else:
        self.current_max_user_id += 1
        user_id = self.current_max_user_id
  
    self.user_to_chunks[user_id] = set(ownedChunks)
    return user_id

3. Memory Leak from Not Cleaning Up After Leave

Forgetting to remove user data when they leave can cause memory leaks and incorrect results in future requests.

Pitfall Example:

def leave(self, userID: int) -> None:
    heappush(self.reusable_user_ids, userID)
    # Bug: Forgot to remove user's chunks!
    # self.user_to_chunks still contains userID's data

Correct Implementation:

def leave(self, userID: int) -> None:
    heappush(self.reusable_user_ids, userID)
    # Must remove user's data to prevent memory leak and wrong results
    if userID in self.user_to_chunks:
        del self.user_to_chunks[userID]

4. Not Validating Chunk IDs

Failing to validate chunk IDs can lead to storing invalid chunks or returning incorrect results.

Pitfall Example:

def request(self, userID: int, chunkID: int) -> List[int]:
    # Bug: No validation of chunkID
    users_with_chunk = []
    for user_id, owned_chunks in self.user_to_chunks.items():
        if chunkID in owned_chunks:
            users_with_chunk.append(user_id)
  
    if users_with_chunk:
        self.user_to_chunks[userID].add(chunkID)  # Could add invalid chunk!
  
    return sorted(users_with_chunk)

5. Using List Instead of Set for Chunk Storage

Using a list to store chunks leads to O(n) lookup time and potential duplicates.

Pitfall Example:

def __init__(self, m: int):
    self.user_to_chunks = defaultdict(list)  # Bug: Using list instead of set

def join(self, ownedChunks: List[int]) -> int:
    # ...
    self.user_to_chunks[user_id] = ownedChunks  # O(n) lookups later!

Correct Approach:

def __init__(self, m: int):
    self.user_to_chunks = defaultdict(set)  # Use set for O(1) lookups

def join(self, ownedChunks: List[int]) -> int:
    # ...
    self.user_to_chunks[user_id] = set(ownedChunks)  # Convert to set
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