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
3 * 104
calls will be made to postTweet
, getNewsFeed
, follow
, and unfollow
.This problem requires designing a simplified Twitter system with functionalities for posting tweets, following/unfollowing users, and retrieving a user's news feed. The key is efficient handling of tweet ordering and following relationships.
We utilize several data structures to achieve optimal performance:
user_tweets
(or userTweets
in Java): A dictionary (or HashMap) mapping user IDs to lists of their tweet IDs. This allows quick access to a user's tweets.
user_following
(or userFollowing
in Java): A dictionary (or HashMap) mapping user IDs to sets of the users they follow. This enables efficient checking of following relationships.
tweets
(or tweets
in Java): A dictionary (or HashMap) mapping tweet IDs to their posting timestamps. This is crucial for ordering tweets in the news feed.
time
: A counter to generate unique timestamps for each tweet.
The core logic involves:
postTweet
: Appends the new tweet ID to the user's tweet list and records its timestamp.getNewsFeed
: Gathers tweets from the user and all users they follow. It then uses a priority queue (or a sorting mechanism) to order tweets by timestamp (most recent first), limiting the result to the top 10.follow
and unfollow
: Update the user_following
dictionary accordingly.from collections import defaultdict
from heapq import nlargest
class Twitter:
def __init__(self):
self.user_tweets = defaultdict(list) # User ID -> List of Tweet IDs
self.user_following = defaultdict(set) # User ID -> Set of followed User IDs
self.tweets = {} # Tweet ID -> Timestamp
self.time = 0
def postTweet(self, userId: int, tweetId: int) -> None:
self.time += 1
self.user_tweets[userId].append(tweetId)
self.tweets[tweetId] = self.time
def getNewsFeed(self, userId: int) -> List[int]:
following = self.user_following[userId]
users = set(following)
users.add(userId) # Include the user's own tweets
tweets_to_sort = []
for user in users:
tweets_to_sort.extend(self.user_tweets[user]) #Combine all tweets
#Efficiently get top 10 tweets by timestamp
return nlargest(10, tweets_to_sort, key=lambda tweetId: self.tweets[tweetId])
def follow(self, followerId: int, followeeId: int) -> None:
self.user_following[followerId].add(followeeId)
def unfollow(self, followerId: int, followeeId: int) -> None:
if followeeId in self.user_following[followerId]:
self.user_following[followerId].remove(followeeId)
import java.util.*;
class Twitter {
private Map<Integer, List<Integer>> userTweets;
private Map<Integer, Set<Integer>> userFollowing;
private Map<Integer, Integer> tweets;
private int time;
public Twitter() {
userTweets = new HashMap<>();
userFollowing = new HashMap<>();
tweets = new HashMap<>();
time = 0;
}
public void postTweet(int userId, int tweetId) {
userTweets.computeIfAbsent(userId, k -> new ArrayList<>()).add(tweetId);
tweets.put(tweetId, ++time);
}
public List<Integer> getNewsFeed(int userId) {
Set<Integer> following = userFollowing.getOrDefault(userId, new HashSet<>());
following.add(userId); // Include user's own tweets
PriorityQueue<Integer> pq = new PriorityQueue<>((a, b) -> tweets.get(b) - tweets.get(a)); //Max heap for recent tweets
for (int user : following) {
List<Integer> userTweetsList = userTweets.getOrDefault(user, new ArrayList<>());
for (int tweetId : userTweetsList) {
pq.offer(tweetId);
if (pq.size() > 10) {
pq.poll(); //Keep only top 10
}
}
}
List<Integer> result = new ArrayList<>(pq);
Collections.reverse(result); //Reverse for most recent first.
return result;
}
public void follow(int followerId, int followeeId) {
userFollowing.computeIfAbsent(followerId, k -> new HashSet<>()).add(followeeId);
}
public void unfollow(int followerId, int followeeId) {
userFollowing.getOrDefault(followerId, new HashSet<>()).remove(followeeId);
}
}
Time Complexity:
postTweet
: O(1) on average.getNewsFeed
: O(M log K), where M is the total number of tweets from followed users and K is 10 (the maximum number of tweets returned). The log K comes from the priority queue operations.follow
and unfollow
: O(1) on average.Space Complexity: O(N + M), where N is the number of users and M is the total number of tweets. The space is dominated by the user_tweets
and tweets
maps.
The use of a priority queue in getNewsFeed
ensures that we efficiently find the 10 most recent tweets without having to sort the entire list of tweets. This optimization is crucial for handling a large number of tweets.