150NeetCode 150

69.Design Twitter

MediumHeap / Priority Queue ยท 6/7

Problem

Design a simplified Twitter where users post tweets, follow/unfollow each other, and see the 10 most recent tweets in their news feed. Implement Twitter:

  • Twitter() initializes the object.
  • void postTweet(int userId, int tweetId) โ€” user userId posts a new tweet with unique id tweetId.
  • List<Integer> getNewsFeed(int userId) โ€” up to 10 most recent tweet ids posted by the user or anyone they follow, ordered most recent first.
  • void follow(int followerId, int followeeId) and void unfollow(int followerId, int followeeId).

Examples

Input:  ["Twitter","postTweet","getNewsFeed","follow","postTweet","getNewsFeed","unfollow","getNewsFeed"]
        [[],[1,5],[1],[1,2],[2,6],[1],[1,2],[1]]
Output: [null,null,[5],null,null,[6,5],null,[5]]
Explanation: After following user 2, user 1 sees tweet 6 (newer) then 5. After unfollowing, only 5.
Input:  ["Twitter","postTweet","postTweet","getNewsFeed","follow","getNewsFeed"]
        [[],[1,1],[2,2],[2],[2,1],[2]]
Output: [null,null,null,[2],null,[2,1]]
Explanation: User 2 initially sees only their own tweet; after following 1, both, newest first.

Constraints

  • 1 <= userId, followerId, followeeId <= 500
  • 0 <= tweetId <= 10^4
  • All tweets have unique ids.
  • At most 3 * 10^4 calls in total to postTweet, getNewsFeed, follow, and unfollow.
  • A user cannot follow themselves (test data never does this, but the code should not break if it happens).

๐Ÿ’ก Key Insight

Each user's own tweets are naturally sorted by time (appended in posting order). A news feed is therefore a merge of F sorted lists where we only need the top 10 โ€” exactly the "merge k sorted lists" pattern. Seed a max-heap with each followee's newest tweet, pop the most recent, and push that user's next-older tweet; stop after 10 pops.

Brute Force (Global Timeline Scan)

TimeO(1) for postTweet, follow, unfollow; O(T) for getNewsFeed, where T is the total number of tweets (a user who follows nobody active may scan everything).SpaceO(T + E)

Intuition

Store every tweet in one global list in posting order. To build a feed, walk the list backwards (newest first) and keep tweets authored by the user or someone they follow, until 10 are collected.

Algorithm

  1. postTweet: append [userId, tweetId] to timeline.
  2. follow / unfollow: add/remove in following.get(followerId) (a set).
  3. getNewsFeed: scan timeline from the end; include a tweet if its author is userId or in the follow set; stop at 10.

Visualization

timeline (oldest -> newest): [u1:5] [u3:7] [u2:6]
user 1 follows {2}

scan from newest: u2:6 keep, u3:7 skip, u1:5 keep -> [6, 5]

Code

java
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

class Twitter {
    private final List<int[]> timeline = new ArrayList<>(); // [userId, tweetId]
    private final Map<Integer, Set<Integer>> following = new HashMap<>();

    public Twitter() {
    }

    public void postTweet(int userId, int tweetId) {
        timeline.add(new int[] {userId, tweetId});
    }

    public List<Integer> getNewsFeed(int userId) {
        Set<Integer> followees = following.getOrDefault(userId, new HashSet<>());
        List<Integer> feed = new ArrayList<>();
        for (int i = timeline.size() - 1; i >= 0 && feed.size() < 10; i--) {
            int author = timeline.get(i)[0];
            if (author == userId || followees.contains(author)) {
                feed.add(timeline.get(i)[1]);
            }
        }
        return feed;
    }

    public void follow(int followerId, int followeeId) {
        following.computeIfAbsent(followerId, k -> new HashSet<>()).add(followeeId);
    }

    public void unfollow(int followerId, int followeeId) {
        Set<Integer> set = following.get(followerId);
        if (set != null) {
            set.remove(followeeId);
        }
    }
}

Complexity

  • Time: O(1) for postTweet, follow, unfollow; O(T) for getNewsFeed, where T is the total number of tweets (a user who follows nobody active may scan everything).
  • Space: O(T + E) โ€” all tweets plus follow edges E.

Complexity Comparison

Approach postTweet getNewsFeed Space
Global Timeline Scan O(1) O(T) O(T + E)
Per-User Lists + Sort O(1) O(R log R) O(T + E)
Per-User Lists + Heap Merge O(1) O(F log F) O(T + E)

Common Pitfalls

  • Ordering by tweetId instead of a timestamp โ€” tweet ids are unique but not increasing with time.
  • Forgetting the user's own tweets, or counting them twice when the user also appears in their follow set.
  • unfollow on a user who never followed anyone โ€” guard against a null set.
  • Mutating the stored follow set when adding userId for the feed โ€” copy it first, otherwise unfollow(self) semantics get messy.