🧠
Better Approach (Use Max Heap for K-Way Merge)
💡 This approach improves efficiency by merging the tweet lists of followed users using a max heap, avoiding sorting all tweets at once.
Intuition
Each user’s tweets are stored in descending order by timestamp. To get the news feed, we perform a k-way merge of these sorted lists using a max heap to efficiently find the next most recent tweet.
Algorithm
- Store each user’s tweets in a linked list or array in descending order of timestamp.
- Maintain a map of followees for each user.
- For getNewsFeed(userId), initialize a max heap and push the most recent tweet from each followee and the user.
- Pop from the heap to get the next most recent tweet, then push the next tweet from the same user’s list if available.
- Repeat until 10 tweets are collected or heap is empty.
💡 The key insight is to merge sorted lists efficiently using a heap, which is faster than sorting all tweets together.
import heapq
class Twitter:
def __init__(self):
self.time = 0
self.tweets = {} # userId -> list of (time, tweetId)
self.followees = {} # userId -> set of followeeIds
def postTweet(self, userId: int, tweetId: int) -> None:
self.time += 1
if userId not in self.tweets:
self.tweets[userId] = []
self.tweets[userId].insert(0, (self.time, tweetId)) # newest at front
def getNewsFeed(self, userId: int) -> list:
users = self.followees.get(userId, set()).copy()
users.add(userId)
heap = [] # max heap simulated by pushing negative time
indices = {} # userId -> current index in tweets list
for u in users:
if u in self.tweets and self.tweets[u]:
indices[u] = 0
time, tweetId = self.tweets[u][0]
heapq.heappush(heap, (-time, tweetId, u))
res = []
while heap and len(res) < 10:
neg_time, tweetId, u = heapq.heappop(heap)
res.append(tweetId)
indices[u] += 1
if indices[u] < len(self.tweets[u]):
time, nextTweetId = self.tweets[u][indices[u]]
heapq.heappush(heap, (-time, nextTweetId, u))
return res
def follow(self, followerId: int, followeeId: int) -> None:
if followerId not in self.followees:
self.followees[followerId] = set()
self.followees[followerId].add(followeeId)
def unfollow(self, followerId: int, followeeId: int) -> None:
if followerId in self.followees:
self.followees[followerId].discard(followeeId)
# Driver code
if __name__ == '__main__':
twitter = Twitter()
twitter.postTweet(1, 5)
print(twitter.getNewsFeed(1)) # [5]
twitter.follow(1, 2)
twitter.postTweet(2, 6)
print(twitter.getNewsFeed(1)) # [6, 5]
Line Notes
self.tweets[userId].insert(0, (self.time, tweetId))Insert newest tweet at front for descending order
users = self.followees.get(userId, set()).copy()Get all followees safely
users.add(userId)Include user's own tweets
heap = []Initialize max heap (using negative time)
indices = {}Track current index in each user's tweet list
heapq.heappush(heap, (-time, tweetId, u))Push most recent tweet from each user
neg_time, tweetId, u = heapq.heappop(heap)Pop tweet with max timestamp
indices[u] += 1Move to next tweet in user's list
if indices[u] < len(self.tweets[u])If more tweets exist, push next one to heap
import java.util.*;
class Twitter {
private static int time = 0;
private Map<Integer, LinkedList<int[]>> tweets; // userId -> list of [time, tweetId]
private Map<Integer, Set<Integer>> followees; // userId -> set of followees
public Twitter() {
tweets = new HashMap<>();
followees = new HashMap<>();
}
public void postTweet(int userId, int tweetId) {
time++;
tweets.putIfAbsent(userId, new LinkedList<>());
tweets.get(userId).addFirst(new int[]{time, tweetId});
}
public List<Integer> getNewsFeed(int userId) {
Set<Integer> users = new HashSet<>();
if (followees.containsKey(userId)) {
users.addAll(followees.get(userId));
}
users.add(userId);
PriorityQueue<int[]> heap = new PriorityQueue<>((a, b) -> b[0] - a[0]);
Map<Integer, Integer> indices = new HashMap<>();
for (int u : users) {
if (tweets.containsKey(u) && !tweets.get(u).isEmpty()) {
indices.put(u, 0);
int[] tweet = tweets.get(u).get(0);
heap.offer(new int[]{tweet[0], tweet[1], u});
}
}
List<Integer> res = new ArrayList<>();
while (!heap.isEmpty() && res.size() < 10) {
int[] top = heap.poll();
res.add(top[1]);
int u = top[2];
int idx = indices.get(u) + 1;
if (idx < tweets.get(u).size()) {
indices.put(u, idx);
int[] nextTweet = tweets.get(u).get(idx);
heap.offer(new int[]{nextTweet[0], nextTweet[1], u});
}
}
return res;
}
public void follow(int followerId, int followeeId) {
followees.putIfAbsent(followerId, new HashSet<>());
followees.get(followerId).add(followeeId);
}
public void unfollow(int followerId, int followeeId) {
if (followees.containsKey(followerId)) {
followees.get(followerId).remove(followeeId);
}
}
// Driver code
public static void main(String[] args) {
Twitter twitter = new Twitter();
twitter.postTweet(1, 5);
System.out.println(twitter.getNewsFeed(1)); // [5]
twitter.follow(1, 2);
twitter.postTweet(2, 6);
System.out.println(twitter.getNewsFeed(1)); // [6, 5]
}
}
Line Notes
tweets.get(userId).addFirst(new int[]{time, tweetId});Add newest tweet at front for descending order
Set<Integer> users = new HashSet<>();Collect all users to consider
users.add(userId);Include user's own tweets
PriorityQueue<int[]> heap = new PriorityQueue<>((a, b) -> b[0] - a[0]);Max heap by timestamp
indices.put(u, 0);Track current index in user's tweet list
heap.offer(new int[]{tweet[0], tweet[1], u});Push most recent tweet from user
int[] top = heap.poll();Pop tweet with max timestamp
indices.put(u, idx);Update index for next tweet
heap.offer(new int[]{nextTweet[0], nextTweet[1], u});Push next tweet from same user
#include <iostream>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <queue>
using namespace std;
class Twitter {
int time;
unordered_map<int, vector<pair<int,int>>> tweets; // userId -> vector of (time, tweetId)
unordered_map<int, unordered_set<int>> followees; // userId -> set of followees
public:
Twitter() {
time = 0;
}
void postTweet(int userId, int tweetId) {
time++;
tweets[userId].insert(tweets[userId].begin(), {time, tweetId});
}
vector<int> getNewsFeed(int userId) {
unordered_set<int> users;
if (followees.count(userId)) {
users = followees[userId];
}
users.insert(userId);
struct cmp {
bool operator()(const tuple<int,int,int>& a, const tuple<int,int,int>& b) {
return get<0>(a) < get<0>(b); // max heap by time
}
};
priority_queue<tuple<int,int,int>, vector<tuple<int,int,int>>, cmp> heap;
unordered_map<int,int> indices;
for (int u : users) {
if (tweets.count(u) && !tweets[u].empty()) {
indices[u] = 0;
auto& t = tweets[u][0];
heap.emplace(t.first, t.second, u);
}
}
vector<int> res;
while (!heap.empty() && (int)res.size() < 10) {
auto [timeStamp, tweetId, u] = heap.top(); heap.pop();
res.push_back(tweetId);
indices[u]++;
if (indices[u] < (int)tweets[u].size()) {
auto& t = tweets[u][indices[u]];
heap.emplace(t.first, t.second, u);
}
}
return res;
}
void follow(int followerId, int followeeId) {
followees[followerId].insert(followeeId);
}
void unfollow(int followerId, int followeeId) {
if (followees.count(followerId)) {
followees[followerId].erase(followeeId);
}
}
};
// Driver code
int main() {
Twitter twitter;
twitter.postTweet(1, 5);
vector<int> feed1 = twitter.getNewsFeed(1);
for (int id : feed1) cout << id << " ";
cout << endl; // 5
twitter.follow(1, 2);
twitter.postTweet(2, 6);
vector<int> feed2 = twitter.getNewsFeed(1);
for (int id : feed2) cout << id << " ";
cout << endl; // 6 5
return 0;
}
Line Notes
tweets[userId].insert(tweets[userId].begin(), {time, tweetId});Insert newest tweet at front for descending order
unordered_set<int> users;Collect all users to consider
users.insert(userId);Include user's own tweets
priority_queue<tuple<int,int,int>, ...> heap;Max heap by timestamp
indices[u] = 0;Track current index in user's tweet list
heap.emplace(t.first, t.second, u);Push next tweet from same user
auto [timeStamp, tweetId, u] = heap.top();Pop tweet with max timestamp
indices[u]++;Move to next tweet
class Twitter {
constructor() {
this.time = 0;
this.tweets = new Map(); // userId -> array of [time, tweetId]
this.followees = new Map(); // userId -> set of followees
}
postTweet(userId, tweetId) {
this.time++;
if (!this.tweets.has(userId)) {
this.tweets.set(userId, []);
}
this.tweets.get(userId).unshift([this.time, tweetId]); // newest at front
}
getNewsFeed(userId) {
const users = new Set();
if (this.followees.has(userId)) {
for (const f of this.followees.get(userId)) {
users.add(f);
}
}
users.add(userId);
const heap = [];
const indices = new Map();
for (const u of users) {
if (this.tweets.has(u) && this.tweets.get(u).length > 0) {
indices.set(u, 0);
const [time, tweetId] = this.tweets.get(u)[0];
heap.push([-time, tweetId, u]);
}
}
// Heapify
heap.sort((a, b) => a[0] - b[0]);
const res = [];
while (heap.length > 0 && res.length < 10) {
const [negTime, tweetId, u] = heap.shift();
res.push(tweetId);
let idx = indices.get(u) + 1;
if (idx < this.tweets.get(u).length) {
indices.set(u, idx);
const [time, nextTweetId] = this.tweets.get(u)[idx];
// Insert in sorted order to maintain min-heap
let inserted = false;
for (let i = 0; i < heap.length; i++) {
if (-time < heap[i][0]) {
heap.splice(i, 0, [-time, nextTweetId, u]);
inserted = true;
break;
}
}
if (!inserted) {
heap.push([-time, nextTweetId, u]);
}
}
}
return res;
}
follow(followerId, followeeId) {
if (!this.followees.has(followerId)) {
this.followees.set(followerId, new Set());
}
this.followees.get(followerId).add(followeeId);
}
unfollow(followerId, followeeId) {
if (this.followees.has(followerId)) {
this.followees.get(followerId).delete(followeeId);
}
}
}
// Driver code
const twitter = new Twitter();
twitter.postTweet(1, 5);
console.log(twitter.getNewsFeed(1)); // [5]
twitter.follow(1, 2);
twitter.postTweet(2, 6);
console.log(twitter.getNewsFeed(1)); // [6, 5]
Line Notes
this.tweets.get(userId).unshift([this.time, tweetId]);Add newest tweet at front for descending order
const users = new Set();Collect all users to consider
users.add(userId);Include user's own tweets
heap.push([-time, tweetId, u]);Push most recent tweet with negative time for max heap
heap.sort((a, b) => a[0] - b[0]);Heapify by sorting (inefficient but simple)
const [negTime, tweetId, u] = heap.shift();Pop tweet with max timestamp
indices.set(u, idx);Update index for next tweet
heap.splice(i, 0, [-time, nextTweetId, u]);Insert next tweet maintaining heap order
this.followees.get(followerId).add(followeeId);Add followee
this.followees.get(followerId).delete(followeeId);Remove followee if exists
TimeO(N log F) where N is number of tweets retrieved (up to 10) and F is number of followees + 1
SpaceO(F) for heap and indices
Heap merges k sorted lists efficiently, only processing up to 10 tweets per getNewsFeed call.
💡 This approach is much faster than brute force because it avoids sorting all tweets, only merging the top tweets from each user.
Interview Verdict: Accepted / Efficient for large inputs
This is the standard approach to implement in interviews for this problem.