你需要实现一个简化版的Twitter类,核心是记录用户推文(带时间戳)、管理关注关系、聚合并排序关注人(含自己)的最近10条推文。关键要保证推文按时间从新到旧排序,且关注/取消关注操作能正确影响新闻推送结果。
解题思路
-
数据结构设计 :
- 全局时间戳:静态计数器,每次发推文自增,用于标记推文的新旧(时间戳越大越新);
- 推文存储 :
Map<Integer, List<Tweet>>,key为用户ID,value为该用户发布的所有推文(含tweetId和时间戳); - 关注关系存储 :
Map<Integer, Set<Integer>>,key为关注者ID,value为其关注的用户ID集合(Set保证无重复,且过滤"关注自己"的无效操作)。
-
核心操作逻辑 :
操作 实现方式 postTweet 为用户添加新推文,时间戳自增,保证每条推文有唯一且递增的时间标记; follow 为关注者添加关注对象(过滤"关注自己"的情况); unfollow 从关注者的关注列表中移除指定对象(过滤"取消关注自己"的情况); getNewsFeed 收集用户自己+关注的人的所有推文 → 按时间戳降序排序 → 取前10条推文ID。
完整Java代码(AC级性能,匹配示例)
java
import java.util.*;
public class Twitter {
// 静态内部类:存储推文(tweetId + 时间戳)
private static class Tweet {
int tweetId;
int timestamp;
public Tweet(int tweetId, int timestamp) {
this.tweetId = tweetId;
this.timestamp = timestamp;
}
}
// 全局时间戳:每次发推文自增,标记推文新旧(越大越新)
private static int timestamp = 0;
// 用户ID -> 该用户发布的所有推文(按发布时间从旧到新存储)
private Map<Integer, List<Tweet>> userTweets;
// 关注者ID -> 关注的用户ID集合(避免重复关注)
private Map<Integer, Set<Integer>> followRelations;
// 初始化Twitter对象
public Twitter() {
userTweets = new HashMap<>();
followRelations = new HashMap<>();
}
// 发布推文:userId发布tweetId的推文
public void postTweet(int userId, int tweetId) {
// 若用户无推文记录,初始化空列表
userTweets.computeIfAbsent(userId, k -> new ArrayList<>());
// 时间戳自增,创建新推文并加入列表
userTweets.get(userId).add(new Tweet(tweetId, ++timestamp));
}
// 获取新闻推送:返回用户关注人(含自己)的最近10条推文ID(从新到旧)
public List<Integer> getNewsFeed(int userId) {
// 步骤1:收集所有相关用户ID(自己 + 关注的人)
Set<Integer> relevantUsers = new HashSet<>();
relevantUsers.add(userId); // 必须包含自己
// 若有关注关系,添加关注的人
if (followRelations.containsKey(userId)) {
relevantUsers.addAll(followRelations.get(userId));
}
// 步骤2:收集所有相关用户的推文
List<Tweet> allTweets = new ArrayList<>();
for (int uid : relevantUsers) {
if (userTweets.containsKey(uid)) {
allTweets.addAll(userTweets.get(uid));
}
}
// 步骤3:按时间戳降序排序(新推文在前)
allTweets.sort((t1, t2) -> Integer.compare(t2.timestamp, t1.timestamp));
// 步骤4:取前10条,提取tweetId
List<Integer> result = new ArrayList<>();
int limit = Math.min(10, allTweets.size());
for (int i = 0; i < limit; i++) {
result.add(allTweets.get(i).tweetId);
}
return result;
}
// 关注操作:followerId关注followeeId(不能关注自己)
public void follow(int followerId, int followeeId) {
// 过滤"关注自己"的无效操作
if (followerId == followeeId) {
return;
}
// 若关注者无关注记录,初始化空集合
followRelations.computeIfAbsent(followerId, k -> new HashSet<>());
// 添加关注对象
followRelations.get(followerId).add(followeeId);
}
// 取消关注:followerId取消关注followeeId(不能取消关注自己)
public void unfollow(int followerId, int followeeId) {
// 过滤"取消关注自己"的无效操作
if (followerId == followeeId) {
return;
}
// 若关注者有关注记录,移除关注对象
if (followRelations.containsKey(followerId)) {
followRelations.get(followerId).remove(followeeId);
}
}
// 测试用例(匹配题目示例)
public static void main(String[] args) {
Twitter twitter = new Twitter();
twitter.postTweet(1, 5); // 用户1发布推文5
System.out.println(twitter.getNewsFeed(1)); // 输出[5]
twitter.follow(1, 2); // 用户1关注用户2
twitter.postTweet(2, 6); // 用户2发布推文6
System.out.println(twitter.getNewsFeed(1)); // 输出[6,5]
twitter.unfollow(1, 2); // 用户1取消关注用户2
System.out.println(twitter.getNewsFeed(1)); // 输出[5]
}
}
代码关键部分解释
- Tweet内部类 :
封装推文的核心属性(tweetId唯一标识,timestamp标记时间),是数据聚合和排序的基础。 - 全局时间戳 :
静态变量保证所有推文的时间戳全局唯一且递增,解决"如何判断推文新旧"的核心问题。 - postTweet方法 :
computeIfAbsent简化"用户无推文记录时初始化列表"的逻辑;- 时间戳先自增再赋值,保证第一条推文的时间戳为1,后续依次递增。
- getNewsFeed方法 :
- 先收集"自己+关注的人"的所有相关用户ID,避免遗漏;
- 聚合所有推文后按时间戳降序排序(
(t1, t2) -> Integer.compare(t2.timestamp, t1.timestamp)); - 用
Math.min(10, allTweets.size())保证只取前10条,兼容"推文不足10条"的场景。
- follow/unfollow方法 :
- 先过滤"关注/取消关注自己"的无效操作(符合题目约束);
- Set集合保证关注关系无重复,避免多次关注同一用户导致的冗余。
复杂度分析
| 方法 | 时间复杂度 | 说明 |
|---|---|---|
| postTweet | O(1) | 仅向列表尾部添加元素,无额外开销; |
| follow | O(1) | Set的add操作是O(1); |
| unfollow | O(1) | Set的remove操作是O(1); |
| getNewsFeed | O(N log N) | N为相关推文总数,排序是主要开销(题目约束下N≤500*3e4,但实际用户推文数有限,性能可接受)。 |
进阶优化(可选,针对高频调用场景)
若getNewsFeed调用极频繁,可优化为:
- 为每个用户的推文列表维护按时间戳降序的优先级队列;
- 聚合时用"多路归并"(类似合并k个有序链表)代替全量排序,将时间复杂度从O(N log N)降至O(N + 10 log K)(K为关注的用户数)。
对于题目给定的约束(最多3e4次调用),上述基础版本已完全满足性能要求。
总结
- 核心设计:时间戳标记推文新旧 + Map管理用户推文/关注关系 + 排序聚合最近10条推文;
- 关键约束:过滤"关注自己"的无效操作,保证关注关系无重复;
- 排序逻辑:严格按时间戳降序,取前10条,符合"最近到最远"的要求。