Table of contents
1.
Introduction
2.
Problem Statement
2.1.
Input
2.2.
Output
2.3.
Description
3.
Approach
4.
Program
4.1.
Output
4.2.
Time Complexity
4.3.
Space Complexity
5.
Key Takeaways
Last Updated: Mar 27, 2024

Design Twitter

Author Hussain Kagdi
1 upvote
Career growth poll
Do you think IIT Guwahati certified course can help you in your career?

Introduction

Jack Dorsey once came up with an exciting project, design Twitter. He wants to create a social network, Twitter, where users can post tweets, view news feeds, and follow/unfollow other users. So he first decides to create a simplified version of the idea. He is not sure if he will be able to complete the task. So he asks you to help him. Let us now design a simplified version of Twitter. Are you excited to design Twitter? Let us now look at the problem statement and discuss the solution.

Problem Statement

Create a basic version of Twitter that allows users to submit tweets, follow/unfollow other users, and view the user's news feed's top 10 tweets. Use the Twitter class as follows:

  1. Twitter():  Initialises a new Twitter object.
  2. void postTweet(int userId, int tweetId): It generates a new tweet for the user userId with the ID tweetId. This function will be called with a unique tweetId for each call.
  3. List<Integer>getNewsFeed(int userId): Returns the 10 most recent tweet IDs in the user’s news feed. Each item in the news feed must have been posted by the users who the user followed or the user themself. Tweets must be sorted in chronological order from most recent to least recent.
  4. void follow(int followerId, int followeeId): The user with ID followerId started following the user with ID followeeId.
  5. void unfollow(int followerId, int followeeId): The user with the ID followerId has started unfollowing the person with the ID followeeId.

Input

Twitter()
postTweet(1, 5)
getNewsFeed(1)
follow(1, 2)
postTweet(2, 6)
getNewsFeed(1)
unfollow(1, 2)
getNewsFeed(1)

Output

5
6 5
5

Description

  1. Twitter creates an object of class Twitter. The Twitter class has an empty constructor.
  2. postTweet() takes two arguments, 1 as userId, and 5 as tweetId, and adds a tweet with the id 5 to the user with Id 1. Now users with userId 1 and all the users who follow him can see that tweet.
  3. getNewsFeed() takes 1 as an argument i.e. userId and returns ten most recent tweets for this user. As the tweets are less than ten, then we return only one tweet with tweetId 5.
  4. follow() takes 1 and 2 as arguments i.e. followerId, and followeeId. The user with jobId 1 will now become a follower of the user with userId 2, and he can see his tweets.
  5. Again postTweet() takes two arguments, 2 as userId, and 6 as tweetId, and adds a tweet with the Id 6 to the user with the ID 2.
  6. Again getNewsFeed() takes 1 as an argument i.e. userId and returns tweets for this user. As there are only two tweets with tweetId 5 and 6; and since 6 is the most recent tweet so putting it earlier and 5 later, we will return {6, 5}
  7. Unfollow() takes 1 as followerId, and 2 as followeeId. Now the follower 1 will unfollow followee 2, and he won’t be able to view his tweets anymore.
  8. getNewsFeed() takes 1 as the userId and returns tweets for this user. As user 1 doesn’t follow any other user, so the blog written by him will only be returned, i.e. 5

Approach

  1. PostTweet(): It takes two arguments, USERID, and TWEETID. It adds the pair of USERID and TWEETID into a vector. Now the tweet is available to view to the followers of this user.
  2. getNewsFeed(): It takes a single argument USERID and returns ten latest tweets for that user. The tweets are returned from the users whom this user follows. If there are less than 10 tweets available then we return them. We iterate from the end of the array and get the 10 most latest tweets of the user if available. Iterating from the end of the array guarantees that, we get the latest tweets. Simultaneously, we check if the user follows that user or not. If yes, then we add that tweet to the newsfeed else we continue.
  3. Follow(): It takes two arguments, FOLLOWERID and FOLLOWEEID, and makes the user with FOLLOWERID a follower of the user with FOLLOWEEID. Asymptotically, It takes constant time as we are inserting the follower in the map.
  4. UnFollow(): It takes two arguments, FOLLOWERID and FOLLOWEEID. Now the follower does not follow the followee. Now, we delete the FOLLOWERID from the unordered set of the FOLLOWEEID. Asymptotically, it takes constant time.

Program

#include <unordered_map>
#include <vector>
#include <unordered_set>
#include <iostream>
using namespace std;

class Twitter
{
private:
      // Required data structures.
      unordered_map<int, unordered_set<int> > following;
      vector<pair<int, int> > tweets;

public:
      // Constructor for initialising data structures.
      Twitter()
      {
            following.clear();
            tweets.clear();
      }

      // Function to compose a new tweet.
      bool postTweet(int userId, int tweetId)
      {
            tweets.push_back({userId, tweetId});
            return true;
      }

      /*
          Function to retrieve the ten most recent tweet ids in the user's news feed.
          Each item in the news feed must be posted by users.
          followed or by the user themself. Tweets must be ordered from most.
          recent to least recent.
      */
      vector<int> getNewsFeed(int userId)
      {
            vector<int> ans;
            for (int i = tweets.size() - 1; i >= 0 && ans.size() < 10; i--)
            {
                  if (tweets[i].first == userId)
                  {
                        ans.push_back(tweets[i].second);
                  }
                  else
                  {
                        if (following.find(userId) != following.end())
                        {
                              if (following[userId].find(tweets[i].first) != following[userId].end())
                              {
                                    ans.push_back(tweets[i].second);
                              }
                        }
                  }
            }
            return ans;
      }

      // Follower follows a followee.
      bool follow(int followerId, int followeeId)
      {
            // If the follower already follows the followee, return false.
            if (following[followerId].count(followeeId)) {
                  return false;
            }

            // Otherwise, follow and return true.
            following[followerId].insert(followeeId);
            return true;
      }

      // Follower unfollows a followee.
      bool unfollow(int followerId, int followeeId)
      {
            // If follower already unfollows the followee, return false.
            if (following[followerId].count(followeeId) == 0) {
                  return false;
            }
            // Otherwise, unfollow and return true.
            following[followerId].erase(followeeId);
            return true;
      }
};

int main()
{
      Twitter *twitter = new Twitter();
      bool tweetPosted = false, followed = false, unfollowed = false;
      vector<int> tweets;

      tweetPosted = twitter->postTweet(1, 124);
      cout << (tweetPosted ? "Success" : "Failure") << ": postTweet\n";
      tweetPosted = twitter->postTweet(1, 23);
      cout << (tweetPosted ? "Success" : "Failure") << ": postTweet\n";
      tweetPosted = twitter->postTweet(1, 85);
      cout << (tweetPosted ? "Success" : "Failure") << ": postTweet\n";
      tweetPosted = twitter->postTweet(3, 769);
      cout << (tweetPosted ? "Success" : "Failure") << ": postTweet\n";

      tweets = twitter->getNewsFeed(1);
      cout << (!tweets.empty() ? "Success" : "Failure") << ": getNewsFeed\n";
      for (int i = 0; i < tweets.size(); i++)
      {
            cout << tweets[i] << " ";
      }
      cout << '\n';

      followed = twitter->follow(1, 2);
      cout << (followed ? "Success" : "Failure") << ": follow\n";
      followed = twitter->follow(2, 3);
      cout << (followed ? "Success" : "Failure") << ": follow\n";

      tweetPosted = twitter->postTweet(2, 96);
      cout << (tweetPosted ? "Success" : "Failure") << ": postTweet\n";

      tweets = twitter->getNewsFeed(1);
      cout << (!tweets.empty() ? "Success" : "Failure") << ": getNewsFeed\n";
      for (int i = 0; i < tweets.size(); i++)
      {
            cout << tweets[i] << " ";
      }
      cout << '\n';

      unfollowed = twitter->unfollow(1, 2);
      cout << (unfollowed ? "Success" : "Failure") << ": unfollow\n";

      unfollowed = twitter->unfollow(1, 3);
      cout << (unfollowed ? "Success" : "Failure") << ": unfollow\n";

      tweets = twitter->getNewsFeed(2);
      cout << (!tweets.empty() ? "Success" : "Failure") << ": getNewsFeed\n";
      for (int i = 0; i < tweets.size(); i++)
      {
            cout << tweets[i] << " ";
      }
      cout << '\n';
      return 0;
}
You can also try this code with Online C++ Compiler
Run Code

Output

Success: postTweet
Success: postTweet
Success: postTweet
Success: postTweet
Success: getNewsFeed
85 23 124 
Success: follow
Success: follow
Success: postTweet
Success: getNewsFeed
96 85 23 124 
Success: unfollow
Failure: unfollow
Success: getNewsFeed
96 769 

Time Complexity

  1. getNewsFeed(): O(N), where ‘N’ is the total number of tweets. We iterate through the array of tweets to find the 10 most latest tweets for the user from his followees. In the worst case, we iterate through the entire array.
  2. follow(): O(1).As it just adds an entry to the unordered map. Asymptotically, an unordered map takes constant time for insertion. 
  3. unfollow(): O(1).As it just deleted an entry from the unordered map. Asymptotically, an unordered map takes constant time for deletion. 
  4. postTweet(): O(1). It adds a pair of USERID and TWEETID to the end of the vector. Asymptotically, adding an entry to the back of the array takes constant time.

Space Complexity

O(max(N, U)), where ‘N’ is the number of tweets and ‘U’ is the number of users.
We are adding every ‘TWEET_ID’ and ‘USER_ID’ in the tweets array. So it takes space equal to the number of tweets. Also, we are maintaining a map to store information about the followers of a user. So the overall space complexity is the maximum number of tweets and number of users.

Key Takeaways

In this blog, we discussed an exciting application, namely Design Twitter. We designed our own simplified version of Twitter in C++ where we implemented the functions like post tweet, follow a user, unfollow a user and get news feed. These functions are implemented based on the facilities offered by the actual Twitter. It was an application of Object-Oriented Programming and System Design

Stay tuned for more such problems. To become a systems expert, please head over to Coding Ninjas Studio and practice more such exciting problems.

Live masterclass