HomeLeetcode355. Design Twitter - Leetcode Solutions

355. Design Twitter – Leetcode Solutions

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.

Examples:

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]]

Solution in Python

Approach:

  1. Data Structures:
    • tweets: A dictionary where the key is the userId and the value is a list of tuples. Each tuple contains (timestamp, tweetId) for the tweets posted by the user. This allows us to track all tweets posted by each user.
    • following: A dictionary where the key is the followerId and the value is a set of followeeIds. This allows us to track which users each user follows.
    • time: An integer used to track the order of tweets. Every time a user posts a tweet, we increase this counter to keep track of the order in which tweets were posted.
  2. Methods:
    • postTweet: Adds the tweet to the user’s list of tweets with a timestamp.
    • getNewsFeed: Retrieves the 10 most recent tweets from the user and all the users they follow. We merge the tweets and sort them by timestamp to get the latest ones.
    • follow: Adds the followeeId to the follower’s set of followees.
    • unfollow: Removes the followeeId from the follower’s set of followees (except when a user tries to unfollow themselves).
Python
class Twitter:

    def __init__(self):
        # Dictionary to store all tweets per user: {userId: [(time, tweetId), ...]}
        self.tweets = defaultdict(list)
        # Dictionary to store following relationships: {followerId: {followeeId, ...}}
        self.following = defaultdict(set)
        # A timestamp to track the order of tweets
        self.time = 0

    def postTweet(self, userId: int, tweetId: int) -> None:
        # Add the tweet with a timestamp to the user's tweet list
        self.tweets[userId].append((self.time, tweetId))
        # Increment the timestamp for the next tweet
        self.time += 1

    def getNewsFeed(self, userId: int) -> list:
        # Min-heap to store the top 10 tweets by timestamp
        heap = []
        
        # Get the user's own tweets
        if userId in self.tweets:
            for tweet in self.tweets[userId]:
                heapq.heappush(heap, tweet)
                if len(heap) > 10:
                    heapq.heappop(heap)

        # Get the tweets from the users the current user follows
        for followeeId in self.following[userId]:
            if followeeId in self.tweets:
                for tweet in self.tweets[followeeId]:
                    heapq.heappush(heap, tweet)
                    if len(heap) > 10:
                        heapq.heappop(heap)
        
        # Extract tweets from the heap, ordered from most recent to oldest
        result = []
        while heap:
            result.append(heapq.heappop(heap)[1])
        
        # Return the result in reverse order since we want most recent first
        return result[::-1]

    def follow(self, followerId: int, followeeId: int) -> None:
        # A user cannot follow themselves, so only add if followerId != followeeId
        if followerId != followeeId:
            self.following[followerId].add(followeeId)

    def unfollow(self, followerId: int, followeeId: int) -> None:
        # Remove followeeId from followerId's following list if it exists
        if followeeId in self.following[followerId]:
            self.following[followerId].remove(followeeId)

Explanation of the Code:

  1. __init__():
    • Initializes the tweets dictionary to store tweets for each user.
    • Initializes the following dictionary to store the following relationships (users each user follows).
    • Initializes a time counter to help track the order of tweets globally.
  2. postTweet(userId, tweetId):
    • Adds a tweet with the current timestamp to the list of tweets for the userId.
    • Increments the timestamp after each tweet to ensure that tweets are ordered correctly.
  3. getNewsFeed(userId):
    • Retrieves the 10 most recent tweets from both the user and the users they follow.
    • Uses a min-heap to efficiently keep track of the 10 most recent tweets. We push tweets into the heap, and if the heap exceeds size 10, the smallest (oldest) tweet is popped.
    • At the end, the tweets are extracted from the heap and returned in reverse order to show the most recent tweets first.
  4. follow(followerId, followeeId):
    • Adds followeeId to the set of users that followerId is following, ensuring no duplicates.
  5. unfollow(followerId, followeeId):
    • Removes followeeId from the set of users that followerId is following, unless the user tries to unfollow themselves.

Solution in C++

C++
class Twitter {
    // A struct to represent each tweet with a unique ID and a timestamp for sorting
    struct Tweet {
        int tweetId;
        int timestamp;
        Tweet(int id, int time) : tweetId(id), timestamp(time) {}
    };

    // Keeps track of the timestamp of each tweet (incremental)
    int time;
    
    // Maps a userId to the list of their own tweets (deque is used for fast insertion/removal)
    unordered_map<int, deque<Tweet>> tweets;

    // Maps a userId to the set of userIds they are following
    unordered_map<int, unordered_set<int>> following;

public:
    // Constructor to initialize the Twitter object
    Twitter() {
        time = 0;
    }

    // Function to post a new tweet
    void postTweet(int userId, int tweetId) {
        // Add the tweet to the user's tweet list with the current timestamp
        tweets[userId].emplace_front(tweetId, time++);
        // Keep only the 10 most recent tweets to limit memory usage
        if (tweets[userId].size() > 10) {
            tweets[userId].pop_back();
        }
    }

    // Function to retrieve the 10 most recent tweets for a user's news feed
    vector<int> getNewsFeed(int userId) {
        vector<Tweet> feed;

        // Add the user's own tweets to the news feed
        if (tweets.count(userId)) {
            feed.insert(feed.end(), tweets[userId].begin(), tweets[userId].end());
        }

        // Add tweets from users that this user follows
        for (int followeeId : following[userId]) {
            if (tweets.count(followeeId)) {
                feed.insert(feed.end(), tweets[followeeId].begin(), tweets[followeeId].end());
            }
        }

        // Sort the feed by timestamp (most recent first)
        sort(feed.begin(), feed.end(), [](const Tweet& a, const Tweet& b) {
            return a.timestamp > b.timestamp;
        });

        // Collect the most recent 10 tweet IDs for the feed
        vector<int> result;
        for (int i = 0; i < min(10, (int)feed.size()); ++i) {
            result.push_back(feed[i].tweetId);
        }

        return result;
    }

    // Function to follow another user
    void follow(int followerId, int followeeId) {
        // A user cannot follow themselves
        if (followerId != followeeId) {
            following[followerId].insert(followeeId);
        }
    }

    // Function to unfollow another user
    void unfollow(int followerId, int followeeId) {
        // Remove the followeeId from the set of users that followerId is following
        following[followerId].erase(followeeId);
    }
};

Solution in Javascript

JavaScript
var Twitter = function() {
    // Store user tweets: key is userId, value is an array of tweet objects with tweetId and timestamp
    this.tweets = new Map();
    
    // Store user following information: key is userId, value is a set of followeeIds
    this.following = new Map();
    
    // Global timestamp to simulate tweet posting order
    this.timestamp = 0;
};

/** 
 * @param {number} userId 
 * @param {number} tweetId
 * @return {void}
 */
Twitter.prototype.postTweet = function(userId, tweetId) {
    // If user doesn't have any tweets yet, initialize an array for their tweets
    if (!this.tweets.has(userId)) {
        this.tweets.set(userId, []);
    }
    // Add the new tweet with the tweetId and the current timestamp
    this.tweets.get(userId).push({ tweetId, time: this.timestamp });
    
    // Increment the global timestamp for next tweet
    this.timestamp++;
};

/** 
 * @param {number} userId
 * @return {number[]}
 */
Twitter.prototype.getNewsFeed = function(userId) {
    // Collect tweets from the user and the users they follow
    let feed = [];
    
    // Add user's own tweets to the feed
    if (this.tweets.has(userId)) {
        feed.push(...this.tweets.get(userId));
    }
    
    // Add tweets from followees to the feed
    if (this.following.has(userId)) {
        for (let followeeId of this.following.get(userId)) {
            if (this.tweets.has(followeeId)) {
                feed.push(...this.tweets.get(followeeId));
            }
        }
    }

    // Sort tweets by timestamp in descending order (most recent first)
    feed.sort((a, b) => b.time - a.time);
    
    // Return the 10 most recent tweet IDs
    return feed.slice(0, 10).map(tweet => tweet.tweetId);
};

/** 
 * @param {number} followerId 
 * @param {number} followeeId
 * @return {void}
 */
Twitter.prototype.follow = function(followerId, followeeId) {
    // If the follower isn't following anyone yet, initialize a set for them
    if (!this.following.has(followerId)) {
        this.following.set(followerId, new Set());
    }
    
    // Add followeeId to the follower's followee set
    this.following.get(followerId).add(followeeId);
};

/** 
 * @param {number} followerId 
 * @param {number} followeeId
 * @return {void}
 */
Twitter.prototype.unfollow = function(followerId, followeeId) {
    // If follower is following someone, and they follow the followeeId, remove the followeeId
    if (this.following.has(followerId)) {
        this.following.get(followerId).delete(followeeId);
    }
};

Solution in Java

Java
class Twitter {
    
    // A map to store the tweets of each user (userId -> List of tweetIds)
    private Map<Integer, List<Tweet>> userTweets;
    
    // A map to store the follow relationships (followerId -> Set of followeeIds)
    private Map<Integer, Set<Integer>> userFollows;
    
    // A timestamp to track the order of tweets globally
    private int timestamp;

    // Inner class to represent a tweet with its ID and timestamp
    private class Tweet {
        int tweetId;
        int time;
        
        Tweet(int tweetId, int time) {
            this.tweetId = tweetId;
            this.time = time;
        }
    }

    /** Initialize your Twitter object. */
    public Twitter() {
        userTweets = new HashMap<>();
        userFollows = new HashMap<>();
        timestamp = 0;
    }
    
    /** Compose a new tweet. */
    public void postTweet(int userId, int tweetId) {
        // Initialize user's tweet list if not already done
        if (!userTweets.containsKey(userId)) {
            userTweets.put(userId, new ArrayList<>());
        }
        
        // Add the new tweet to the user's list with a timestamp
        userTweets.get(userId).add(new Tweet(tweetId, timestamp++));
    }
    
    /** Retrieve the 10 most recent tweet IDs in the user's news feed. */
    public List<Integer> getNewsFeed(int userId) {
        // Create a priority queue to get the 10 most recent tweets
        PriorityQueue<Tweet> pq = new PriorityQueue<>((a, b) -> b.time - a.time);
        
        // Get the user's own tweets
        if (userTweets.containsKey(userId)) {
            pq.addAll(userTweets.get(userId));
        }
        
        // Get the tweets of users that this user follows
        if (userFollows.containsKey(userId)) {
            for (int followeeId : userFollows.get(userId)) {
                if (userTweets.containsKey(followeeId)) {
                    pq.addAll(userTweets.get(followeeId));
                }
            }
        }
        
        // Extract the 10 most recent tweets
        List<Integer> result = new ArrayList<>();
        int count = 0;
        while (!pq.isEmpty() && count < 10) {
            result.add(pq.poll().tweetId);
            count++;
        }
        
        return result;
    }
    
    /** Follow another user. */
    public void follow(int followerId, int followeeId) {
        // A user cannot follow themselves
        if (followerId == followeeId) return;
        
        // Initialize the follow set if not already done
        if (!userFollows.containsKey(followerId)) {
            userFollows.put(followerId, new HashSet<>());
        }
        
        // Add the followee to the follower's follow set
        userFollows.get(followerId).add(followeeId);
    }
    
    /** Unfollow another user. */
    public void unfollow(int followerId, int followeeId) {
        // If the follower doesn't follow anyone, no need to proceed
        if (!userFollows.containsKey(followerId)) return;
        
        // Remove the followee from the follower's follow set
        userFollows.get(followerId).remove(followeeId);
    }
}

Subscribe
Notify of

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments

Popular