设计推特

184 阅读3分钟

题目

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/de… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

设计一个简化版的推特(Twitter),可以让用户实现发送推文,关注/取消关注其他用户,能够看见关注人(包括自己)的最近十条推文。你的设计需要支持以下的几个功能:

postTweet(userId, tweetId): 创建一条新的推文 getNewsFeed(userId): 检索最近的十条推文。每个推文都必须是由此用户关注的人或者是用户自己发出的。推文必须按照时间顺序由最近的开始排序。 follow(followerId, followeeId): 关注一个用户 unfollow(followerId, followeeId): 取消关注一个用户 示例:

Twitter twitter = new Twitter();

// 用户1发送了一条新推文 (用户id = 1, 推文id = 5).
twitter.postTweet(1, 5);

// 用户1的获取推文应当返回一个列表,其中包含一个id为5的推文.
twitter.getNewsFeed(1);

// 用户1关注了用户2.
twitter.follow(1, 2);

// 用户2发送了一个新推文 (推文id = 6).
twitter.postTweet(2, 6);

// 用户1的获取推文应当返回一个列表,其中包含两个推文,id分别为 -> [6, 5].
// 推文id6应当在推文id5之前,因为它是在5之后发送的.
twitter.getNewsFeed(1);

// 用户1取消关注了用户2.
twitter.unfollow(1, 2);

// 用户1的获取推文应当返回一个列表,其中包含一个id为5的推文.
// 因为用户1已经不再关注用户2.
twitter.getNewsFeed(1);

思路

题目要求我们实现4个API方法:

  • 发送推特 postTweet(userId, tweetId)
  • 按时间顺序查看最近的10条推特,包含自己和我的关注发送的推特。getNewsFeed(userId)
  • 关注 follow(followerId, followeeId)
  • 取关 unfollow(followerId, followeeId)

你可以声明全局变量Map tweets 保存用户发送的推特,用户和已发送的推特是1对N的关系,但你可以使用单链表保存推特信息。能做到历史发送推特的顺序性,发送新推特只需在表头插入新节点,时间复杂度O(1)。

关注列表并不强调顺序,所以你可以使用 Map<Integer, Set> 保存数据。你可以使全局变量标识发送推特时间戳,每次发送新推特,时间戳加一。

查看最近10条推特方法,你可以构建大顶堆保存推特信息,筛选出10条发送时间最近的推特,即时间戳最大的推特。

代码实现

class Twitter {

    /**用户-推特*/
    private Map<Integer, Tweet> tweet;
    /**关注的人*/
    private Map<Integer, Set<Integer>> followers;
    /**发文的时间戳*/
    private static int timestamp = 0;

    /** Initialize your data structure here. */
    public Twitter() {
        tweet = new HashMap<>();
        followers = new HashMap<>();
    }
    
    /** Compose a new tweet. */
    public void postTweet(int userId, int tweetId) { 
        Tweet oldTwitter = tweet.get(userId);
        Tweet newTwitter = new Tweet(tweetId, ++timestamp);
        newTwitter.next = oldTwitter;
        tweet.put(userId, newTwitter);
    }
    
    /** Retrieve the 10 most recent tweet ids in the user's news feed. Each item in the news feed must be posted by users who the user followed or by the user herself. Tweets must be ordered from most recent to least recent. */
    public List<Integer> getNewsFeed(int userId) {
        PriorityQueue<Tweet> maxHeap = new PriorityQueue<>(new Comparator<Tweet>() {
	          // o2 - o1 构建大顶堆,Java默认是小顶堆
            public int compare(Tweet o1, Tweet o2) {
                return o2.timestamp - o1.timestamp;
            }
        });
        // 把自己发送的推特算进去
        if (tweet.containsKey(userId)) {
            maxHeap.offer(tweet.get(userId));
        }
        // 关注的人发送的推特
        Set<Integer> followerList = followers.getOrDefault(userId, new HashSet<>());
        for (Integer followeeId : followerList) {
            Tweet head = tweet.get(followeeId);
            if (head != null) {
                maxHeap.offer(head);
            }
        }

        List<Integer> ret = new ArrayList<>();
        int count = 0;
        while (!maxHeap.isEmpty() && count < 10) {
            Tweet head = maxHeap.poll();
            ret.add(head.id);
          	// 如果有历史推特,进入大顶堆
            if (head.next != null) {
                maxHeap.offer(head.next);
            }
            count++;
        }
        return ret;
    }
    
    /** Follower follows a followee. If the operation is invalid, it should be a no-op. */
    public void follow(int followerId, int followeeId) {
        if (followerId == followeeId) return;
        Set<Integer> followerList = followers.getOrDefault(followerId, new HashSet<>());
        followerList.add(followeeId);
        followers.put(followerId, followerList);
    }
    
    /** Follower unfollows a followee. If the operation is invalid, it should be a no-op. */
    public void unfollow(int followerId, int followeeId) {
        if (followerId == followeeId) return;
        Set<Integer> followerList = followers.getOrDefault(followerId, new HashSet<>());
        followerList.remove(followeeId);
    }

    private class Tweet {
        /**推特id*/
        public int id;
        /**发文的时间戳*/
        public int timestamp;
        /**前一条推特*/
        public Tweet next;

        public Tweet(int id, int timestamp) {
            this.id = id;
            this.timestamp = timestamp;
        }
    }
}

知识点

  1. 使用单链表维护已发送的推特,天然保持了时间顺序。
  2. 构建大顶堆筛选 TopK 数据
  3. 经典多路归并问题:合并K个升序链表