编程大师-技术-算法-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条,符合"最近到最远"的要求。
相关推荐
少许极端3 小时前
算法奇妙屋(二十八)-递归、回溯与剪枝的综合问题 1
java·算法·深度优先·剪枝·回溯·递归
仰泳的熊猫3 小时前
题目1453:蓝桥杯历届试题-翻硬币
数据结构·c++·算法·蓝桥杯
唐梓航-求职中3 小时前
技术-算法-leetcode-1606. 找到处理最多请求的服务器(易懂版)
服务器·算法·leetcode
啊阿狸不会拉杆3 小时前
《机器学习导论》第 10 章-线性判别式
人工智能·python·算法·机器学习·numpy·lda·线性判别式
会叫的恐龙3 小时前
C++ 核心知识点汇总(第11日)(排序算法)
c++·算法·排序算法
twilight_4693 小时前
机器学习与模式识别——线性回归算法
算法·机器学习·线性回归
玄同7653 小时前
Python Random 模块深度解析:从基础 API 到 AI / 大模型工程化实践
人工智能·笔记·python·学习·算法·语言模型·llm
Pluchon3 小时前
硅基计划4.0 算法 简单模拟实现位图&布隆过滤器
java·大数据·开发语言·数据结构·算法·哈希算法
独断万古他化3 小时前
【算法通关】前缀和:和为 K、和被 K整除、连续数组、矩阵区域和全解
算法·前缀和·矩阵·哈希表