We will use a file-sharing system to share a very large file which consists of m small chunks with IDs from 1 to m.
When users join the system, the system should assign a unique ID to them. The unique ID should be used once for each user, but when a user leaves the system, the ID can be reused again.
Users can request a certain chunk of the file, the system should return a list of IDs of all the users who own this chunk. If the user receives a non-empty list of IDs, they receive the requested chunk successfully.
Implement the FileSharing class:
FileSharing(int m) Initializes the object with a file of m chunks.int join(int[] ownedChunks): A new user joined the system owning some chunks of the file, the system should assign an id to the user which is the smallest positive integer not taken by any other user. Return the assigned id.void leave(int userID): The user with userID will leave the system, you cannot take file chunks from them anymore.int[] request(int userID, int chunkID): The user userID requested the file chunk with chunkID. Return a list of the IDs of all users that own this chunk sorted in ascending order.
Example:
Input: ["FileSharing","join","join","join","request","request","leave","request","leave","join"] [[4],[[1,2]],[[2,3]],[[4]],[1,3],[2,2],[1],[2,1],[2],[[]]] Output: [null,1,2,3,[2],[1,2],null,[],null,1] Explanation: FileSharing fileSharing = new FileSharing(4); // We use the system to share a file of 4 chunks. fileSharing.join([1, 2]); // A user who has chunks [1,2] joined the system, assign id = 1 to them and return 1. fileSharing.join([2, 3]); // A user who has chunks [2,3] joined the system, assign id = 2 to them and return 2. fileSharing.join([4]); // A user who has chunk [4] joined the system, assign id = 3 to them and return 3. fileSharing.request(1, 3); // The user with id = 1 requested the third file chunk, as only the user with id = 2 has the file, return [2] . Notice that user 1 now has chunks [1,2,3]. fileSharing.request(2, 2); // The user with id = 2 requested the second file chunk, users with ids [1,2] have this chunk, thus we return [1,2]. fileSharing.leave(1); // The user with id = 1 left the system, all the file chunks with them are no longer available for other users. fileSharing.request(2, 1); // The user with id = 2 requested the first file chunk, no one in the system has this chunk, we return empty list []. fileSharing.leave(2); // The user with id = 2 left the system. fileSharing.join([]); // A user who doesn't have any chunks joined the system, assign id = 1 to them and return 1. Notice that ids 1 and 2 are free and we can reuse them.
Constraints:
1 <= m <= 1050 <= ownedChunks.length <= min(100, m)1 <= ownedChunks[i] <= mownedChunks are unique.1 <= chunkID <= muserID is guaranteed to be a user in the system if you assign the IDs correctly.104 calls will be made to join, leave and request.leave will have a matching call for join.
Follow-up:
n files where the ith file consists of m[i], what are the changes you have to make?Problem Overview: Design a system that tracks which users own specific file chunks. Users can join with a list of chunks, leave the system, and request chunks from others. The system must reuse the smallest available user ID and return a sorted list of users who own a requested chunk.
Approach 1: Naive Simulation with Sorting (O(n log n) per request)
A straightforward implementation stores chunkId -> list of users and userId -> owned chunks using a hash table. When a user joins, assign the next increasing ID and update mappings for each chunk. For request(), iterate through the stored list of users for that chunk and sort the result before returning it. When users leave, remove them from every chunk list they appear in. This works but repeated sorting and list removals make operations expensive, leading to O(n log n) time per request and O(n) space.
Approach 2: Hash Maps + Min Heap for Reusable IDs (O(log n) operations)
The optimized design uses multiple structures working together. Maintain a chunkOwners map from chunk ID to a sorted set of user IDs, and a userChunks map from user ID to the set of chunks they own. To reuse the smallest available ID, maintain a min heap of released IDs. During join(), either pop from the heap or assign the next new ID, then insert the user into every relevant chunk owner set. For leave(), remove the user from all chunk owner sets and push the freed ID back into the heap. During request(), read the sorted owners directly, return them, and if the list is non-empty add the chunk to the requester. Sorted sets eliminate repeated sorting work while the heap guarantees the smallest available ID. Most updates run in O(log n) time with O(n + m) space where n is users and m is chunk relationships.
Recommended for interviews: The hash map + min heap design is the expected solution. It demonstrates strong system simulation skills and efficient data structure choices. A naive simulation shows you understand the mechanics, but interviewers usually expect the optimized version that reuses IDs with a priority queue and avoids repeated sorting.
| Approach | Time | Space | When to Use |
|---|---|---|---|
| Naive Simulation with Lists | O(n log n) per request | O(n) | Simple implementation when constraints are small and sorting overhead is acceptable |
| Hash Maps + Sorted Sets + Min Heap | O(log n) per update | O(n + m) | Best general solution for large systems where IDs must be reused and chunk owners must stay sorted |
Dropbox system design | Google drive system design | System design file share and upload • Tech Dummies - Narendra Lakshmana Gowda • 269,757 views views
Watch 9 more video solutions →Practice Design a File Sharing System with our built-in code editor and test cases.
Practice on FleetCode