编程大师-技术-算法-leetcode-355. 设计推特

你需要实现一个简化版的Twitter类,核心是记录用户推文(带时间戳)、管理关注关系、聚合并排序关注人(含自己)的最近10条推文。关键要保证推文按时间从新到旧排序,且关注/取消关注操作能正确影响新闻推送结果。

解题思路

  1. 数据结构设计

    • 全局时间戳:静态计数器,每次发推文自增,用于标记推文的新旧(时间戳越大越新);
    • 推文存储Map<Integer, List<Tweet>>,key为用户ID,value为该用户发布的所有推文(含tweetId和时间戳);
    • 关注关系存储Map<Integer, Set<Integer>>,key为关注者ID,value为其关注的用户ID集合(Set保证无重复,且过滤"关注自己"的无效操作)。
  2. 核心操作逻辑

    操作 实现方式
    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]
    }
}

代码关键部分解释

  1. Tweet内部类
    封装推文的核心属性(tweetId 唯一标识,timestamp 标记时间),是数据聚合和排序的基础。
  2. 全局时间戳
    静态变量保证所有推文的时间戳全局唯一且递增,解决"如何判断推文新旧"的核心问题。
  3. postTweet方法
    • computeIfAbsent 简化"用户无推文记录时初始化列表"的逻辑;
    • 时间戳先自增再赋值,保证第一条推文的时间戳为1,后续依次递增。
  4. getNewsFeed方法
    • 先收集"自己+关注的人"的所有相关用户ID,避免遗漏;
    • 聚合所有推文后按时间戳降序排序((t1, t2) -> Integer.compare(t2.timestamp, t1.timestamp));
    • Math.min(10, allTweets.size()) 保证只取前10条,兼容"推文不足10条"的场景。
  5. 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次调用),上述基础版本已完全满足性能要求。

总结

  1. 核心设计:时间戳标记推文新旧 + Map管理用户推文/关注关系 + 排序聚合最近10条推文
  2. 关键约束:过滤"关注自己"的无效操作,保证关注关系无重复;
  3. 排序逻辑:严格按时间戳降序,取前10条,符合"最近到最远"的要求。
相关推荐
大熊背19 分钟前
如何利用Lv值实现三级降帧
算法·自动曝光·lv·isppipeline
大尚来也36 分钟前
驾驭并发:.NET多线程编程的挑战与破局之道
java·前端·算法
向阳而生,一路生花40 分钟前
深入浅出 JDK7 HashMap 源码分析
算法·哈希算法
君义_noip1 小时前
信息学奥赛一本通 4150:【GESP2509七级】⾦币收集 | 洛谷 P14078 [GESP202509 七级] 金币收集
c++·算法·gesp·信息学奥赛·csp-s
摸个小yu1 小时前
【力扣LeetCode热题h100】链表、二叉树
算法·leetcode·链表
汀、人工智能1 小时前
[特殊字符] 第93课:太平洋大西洋水流问题
数据结构·算法·数据库架构·图论·bfs·太平洋大西洋水流问题
ZPC82102 小时前
rviz2 仿真控制器与真实机器人切换
人工智能·算法·机器人
澈2072 小时前
双指针,数组去重
c++·算法
小辉同志2 小时前
207. 课程表
c++·算法·力扣·图论
CheerWWW2 小时前
深入理解计算机系统——位运算、树状数组
笔记·学习·算法·计算机系统