Design Twitter

MEDIUM

Description

Design a simplified version of Twitter where users can post tweets, follow/unfollow another user, and is able to see the 10 most recent tweets in the user's news feed.

Implement the Twitter class:

  • Twitter() Initializes your twitter object.
  • void postTweet(int userId, int tweetId) Composes a new tweet with ID tweetId by the user userId. Each call to this function will be made with a unique tweetId.
  • List<Integer> getNewsFeed(int userId) Retrieves the 10 most recent tweet IDs in the user's news feed. Each item in the news feed must be posted by users who the user followed or by the user themself. Tweets must be ordered from most recent to least recent.
  • void follow(int followerId, int followeeId) The user with ID followerId started following the user with ID followeeId.
  • void unfollow(int followerId, int followeeId) The user with ID followerId started unfollowing the user with ID followeeId.

 

Example 1:

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
Twitter twitter = new Twitter();
twitter.postTweet(1, 5); // User 1 posts a new tweet (id = 5).
twitter.getNewsFeed(1);  // User 1's news feed should return a list with 1 tweet id -> [5]. return [5]
twitter.follow(1, 2);    // User 1 follows user 2.
twitter.postTweet(2, 6); // User 2 posts a new tweet (id = 6).
twitter.getNewsFeed(1);  // User 1's news feed should return a list with 2 tweet ids -> [6, 5]. Tweet id 6 should precede tweet id 5 because it is posted after tweet id 5.
twitter.unfollow(1, 2);  // User 1 unfollows user 2.
twitter.getNewsFeed(1);  // User 1's news feed should return a list with 1 tweet id -> [5], since user 1 is no longer following user 2.

 

Constraints:

  • 1 <= userId, followerId, followeeId <= 500
  • 0 <= tweetId <= 104
  • All the tweets have unique IDs.
  • At most 3 * 104 calls will be made to postTweet, getNewsFeed, follow, and unfollow.
  • A user cannot follow himself.

Approaches

Checkout 3 different approaches to solve Design Twitter. Click on different approaches to view the approach and algorithm in detail.

Brute-force Sorting

This is a straightforward approach where we simulate the process directly. For getNewsFeed, we gather all tweets from the user and the users they follow into a single list, sort this list by time, and then take the 10 most recent ones.

Algorithm

  • Maintain a map userFollows for follow/unfollow relationships (Map<Integer, Set<Integer>>).
  • Maintain a map userTweets to store a list of Tweet objects for each user (Map<Integer, List<Tweet>>). A Tweet object contains its ID and a timestamp.
  • Use a global, static, incrementing timestamp for each new tweet to ensure chronological order.
  • For getNewsFeed(userId):
    1. Create an empty list candidateTweets.
    2. Add all of userId's own tweets to candidateTweets.
    3. For each followeeId that userId follows, add all of their tweets to candidateTweets.
    4. Sort candidateTweets based on timestamp in descending order.
    5. Return the IDs of the first 10 tweets in the sorted list.

We use a Map to store follow relationships (userId -> Set<followeeId>) and another Map to store each user's tweets (userId -> List<Tweet>). A global timestamp is used to record the post time of each tweet, ensuring chronological order. postTweet adds a new tweet with the current timestamp to the user's tweet list. follow and unfollow simply update the follow-relationship map. The main logic is in getNewsFeed. It first identifies all relevant users (the user themselves and their followees). It then iterates through these users, collecting all their tweets into a temporary list. This list is then sorted in descending order based on the tweet's timestamp. Finally, the IDs of the first 10 tweets from the sorted list are returned.

class Twitter {
    private static int timestamp = 0;
    private Map<Integer, Set<Integer>> userFollows;
    private Map<Integer, List<Tweet>> userTweets;

    private class Tweet {
        int id;
        int time;
        public Tweet(int id, int time) {
            this.id = id;
            this.time = time;
        }
    }

    public Twitter() {
        userFollows = new HashMap<>();
        userTweets = new HashMap<>();
    }
    
    public void postTweet(int userId, int tweetId) {
        userTweets.computeIfAbsent(userId, k -> new ArrayList<>()).add(new Tweet(tweetId, timestamp++));
    }
    
    public List<Integer> getNewsFeed(int userId) {
        List<Tweet> allTweets = new ArrayList<>();
        
        // Add user's own tweets
        if (userTweets.containsKey(userId)) {
            allTweets.addAll(userTweets.get(userId));
        }
        
        // Add followees' tweets
        Set<Integer> followees = userFollows.get(userId);
        if (followees != null) {
            for (int followeeId : followees) {
                if (userTweets.containsKey(followeeId)) {
                    allTweets.addAll(userTweets.get(followeeId));
                }
            }
        }
        
        // Sort all collected tweets by time descending
        allTweets.sort((a, b) -> b.time - a.time);
        
        List<Integer> newsFeed = new ArrayList<>();
        for (int i = 0; i < Math.min(10, allTweets.size()); i++) {
            newsFeed.add(allTweets.get(i).id);
        }
        
        return newsFeed;
    }
    
    public void follow(int followerId, int followeeId) {
        userFollows.computeIfAbsent(followerId, k -> new HashSet<>()).add(followeeId);
    }
    
    public void unfollow(int followerId, int followeeId) {
        if (userFollows.containsKey(followerId) && followerId != followeeId) {
            userFollows.get(followerId).remove(followeeId);
        }
    }
}

Complexity Analysis

Time Complexity: `postTweet`, `follow`, `unfollow`: O(1) `getNewsFeed`: O(N log N), where N is the total number of tweets from the user and their followees. Sorting dominates the complexity.Space Complexity: O(U + T), where U is the number of users and T is the total number of tweets stored. `getNewsFeed` also requires O(N) temporary space for the list of candidate tweets, where N is the number of tweets from the user and their followees.

Pros and Cons

Pros:
  • Simple to understand and implement.
  • postTweet, follow, and unfollow operations are very fast, typically O(1).
Cons:
  • getNewsFeed is very inefficient. Its performance degrades significantly as the total number of tweets (N) from the user and their followees increases.

Code Solutions

Checking out 3 solutions in different languages for Design Twitter. Click on different languages to view the code.


Video Solution

Watch the video walkthrough for Design Twitter



Patterns:

Design

Data Structures:

Hash TableLinked ListHeap (Priority Queue)

Subscribe to Scale Engineer newsletter

Learn about System Design, Software Engineering, and interview experiences every week.

No spam, unsubscribe at any time.