AlgoMaster Logo

Design Twitter

mediumFrequency6 min readUpdated June 23, 2026

Understanding the Problem

We are building a simplified social media feed system. There are four operations: posting a tweet, following someone, unfollowing someone, and retrieving a news feed. The work concentrates in getNewsFeed, because it merges tweets from multiple users and returns the 10 most recent ones.

Each user has their own timeline of tweets, ordered by when they were posted. A call to getNewsFeed looks at the timelines of the user and everyone they follow, then picks the 10 most recent tweets across all those timelines. Since each user's timeline is already sorted by recency, this reduces to merging K sorted lists and taking the first 10 elements, where K is the number of users whose tweets matter.

Posting, follow, and unfollow are constant-time updates to a list or a set. The merge in getNewsFeed is the part where the algorithmic choice changes the running time.

Key Constraints:

  • 1 <= userId, followerId, followeeId <= 500 -> At most 500 users, so a follow set per user is cheap.
  • 0 <= tweetId <= 10^4 -> Up to 30,000 total tweets, since every call could be a postTweet.
  • At most 3 * 10^4 calls -> The timestamp counter tops out near 30,000, so a 32-bit integer holds it with no overflow. A feed that rescans every tweet from every followee can do up to 30,000 work per getNewsFeed, even though we only need the 10 newest. The second approach avoids touching tweets we will never return.
  • A user cannot follow himself -> The feed still includes the user's own tweets, but there is no self-follow edge to handle.

Approach 1: Brute Force (Collect and Sort)

Intuition

When getNewsFeed is called, gather every tweet from the user and everyone they follow into one list, sort that list by recency, and return the first 10.

The other operations are direct: postTweet appends to a user's tweet list, follow adds to a set, and unfollow removes from a set.

The cost lives in the sort. If a user follows many people who have each posted many tweets, every feed request sorts a potentially large list, even though only 10 of those tweets survive.

Algorithm

  1. Maintain a Map<Integer, List<int[]>> where each user maps to their list of tweets. Each tweet is stored as [timestamp, tweetId].
  2. Maintain a Map<Integer, Set<Integer>> for follow relationships.
  3. Keep a global timestamp counter, incremented on every postTweet call.
  4. For postTweet(userId, tweetId): append [timestamp, tweetId] to the user's tweet list.
  5. For follow(followerId, followeeId): add followeeId to followerId's follow set.
  6. For unfollow(followerId, followeeId): remove followeeId from followerId's follow set.
  7. For getNewsFeed(userId): collect all tweets from the user and everyone in their follow set into one list. Sort by timestamp descending. Return the first 10 tweet IDs.

Example Walkthrough

tweetMap
1Twitter(): initialize empty tweetMap (userId -> list of [timestamp, tweetId])
followMap
1Twitter(): initialize empty followMap (userId -> set of followeeIds)
result
1Twitter(): no output yet
0
null
1/8

Code

The waste in getNewsFeed is that we collect and sort every tweet from the user and all their followees only to return 10. The next approach uses the fact that each user's tweets are already in chronological order to merge them without sorting everything.

Approach 2: Heap-Based Merge (Optimal)

Intuition

Each user's tweet list is already sorted by time, with the newest at the end since we append in order. So getNewsFeed is merging K sorted lists and returning the top 10 elements, which a max-heap (priority queue) handles directly.

We seed the heap with only the most recent tweet from each relevant user. Then we pop the most recent tweet overall, add it to the result, and push that user's next most recent tweet. We repeat until we have 10 tweets or the heap is empty.

This touches at most 11 tweets per user (one to seed, then one more each time that user's tweet is popped, up to 10 pops total across all users), not every tweet they have ever posted. Each heap operation costs O(log K), where K is the number of users in the feed.

Algorithm

  1. Maintain a Map<Integer, List<int[]>> for tweets (each entry is [timestamp, tweetId]), a Map<Integer, Set<Integer>> for follow relationships, and a global timestamp counter.
  2. postTweet: append [timestamp++, tweetId] to the user's tweet list.
  3. follow: add followeeId to followerId's set.
  4. unfollow: remove followeeId from followerId's set.
  5. getNewsFeed:
    • Build a list of relevant users: the user themselves plus everyone they follow.
    • For each relevant user who has tweets, push their most recent tweet into a max-heap. The heap entry is [timestamp, tweetId, userId, index], where index points to the position in that user's tweet list.
    • Pop from the heap up to 10 times. Each time we pop, add the tweetId to the result, then push the same user's next most recent tweet (if it exists) into the heap.

Example Walkthrough

tweetMap
1Twitter(): initialize empty tweetMap (userId -> list of [timestamp, tweetId])
followMap
1Twitter(): initialize empty followMap (userId -> set of followeeIds)
maxHeap
1Twitter(): heap not used until getNewsFeed is called
[]
result
1Twitter(): no output yet
0
null
1/8

Code