编程大师-技术-算法-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条,符合"最近到最远"的要求。
相关推荐
倒头就睡的小比特1 天前
算法竞赛C++常用的STL
c++·算法
小羊没烦恼!1 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
晨米酱1 天前
AGENTS.md:Agent 的上下文策略层
面试·架构·agent
猎头南楼1 天前
知识社区推荐系统实践:新用户冷启动与长短期兴趣建模的挑战 资深推荐算法工程师
人工智能·深度学习·算法·机器学习
旖旎夜光1 天前
力控面试题 01.01: 判定字符是否唯一(位运算) —— 题解
c++·学习·算法·leetcode·力控
wzdark1 天前
大规模并行计算中的负载均衡算法研究4
算法
Because_of_Her11 天前
并查集-听课笔记
笔记·算法·并查集
码流子1 天前
高速公路安全监测实践:碰撞监测预警+物联网底座,从感知到处置的闭环
大数据·人工智能·物联网·算法·架构
彧azz1 天前
Linux 环境下 Redis 学习总结:数据类型、持久化、锁、事务、主从与缓存问题
linux·redis·笔记·学习·面试
another heaven1 天前
【算法/C++ MD5算法能否逆解码?原理、C++实现与同类哈希算法对比】
c++·算法·哈希算法