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.
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.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.
Map<Integer, List<int[]>> where each user maps to their list of tweets. Each tweet is stored as [timestamp, tweetId].Map<Integer, Set<Integer>> for follow relationships.timestamp counter, incremented on every postTweet call.postTweet(userId, tweetId): append [timestamp, tweetId] to the user's tweet list.follow(followerId, followeeId): add followeeId to followerId's follow set.unfollow(followerId, followeeId): remove followeeId from followerId's follow set.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.postTweet, follow, and unfollow. O(T log T) for getNewsFeed, where T is the total number of tweets from the user and all their followees. We collect all relevant tweets and sort them.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.
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.
The globally most recent unselected tweet is always one user's current frontier tweet. Within a single user, tweets are sorted by time, so a user's newest unselected tweet is more recent than all of that user's older ones. Across users, the heap holds exactly one frontier tweet per user, so its maximum is the most recent tweet not yet selected. After we pop a user's frontier and replace it with that user's next-older tweet, the invariant still holds. Repeating this 10 times yields the 10 most recent tweets in order.
Map<Integer, List<int[]>> for tweets (each entry is [timestamp, tweetId]), a Map<Integer, Set<Integer>> for follow relationships, and a global timestamp counter.postTweet: append [timestamp++, tweetId] to the user's tweet list.follow: add followeeId to followerId's set.unfollow: remove followeeId from followerId's set.getNewsFeed:[timestamp, tweetId, userId, index], where index points to the position in that user's tweet list.postTweet, follow, and unfollow. O(K log K) for getNewsFeed, where K is the number of users in the feed (the user + their followees). We push K items into the heap (each O(log K)), then pop at most 10 times (each O(log K)). Since we pop at most 10 times, this is O(K log K + 10 log K) = O(K log K).getNewsFeed (at most the number of followees + 1).