355.设计推特

【LetMeFly】355.设计推特

力扣题目链接:https://leetcode.cn/problems/design-twitter/

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

实现 Twitter 类:

  • Twitter() 初始化简易版推特对象
  • void postTweet(int userId, int tweetId) 根据给定的 tweetIduserId 创建一条新推文。每次调用此函数都会使用一个不同的 tweetId
  • List<Integer> getNewsFeed(int userId) 检索当前用户新闻推送中最近  10 条推文的 ID 。新闻推送中的每一项都必须是由用户关注的人或者是用户自己发布的推文。推文必须 按照时间顺序由最近到最远排序
  • void follow(int followerId, int followeeId) ID 为 followerId 的用户开始关注 ID 为 followeeId 的用户。
  • void unfollow(int followerId, int followeeId) ID 为 followerId 的用户不再关注 ID 为 followeeId 的用户。

 

示例:

输入
["Twitter", "postTweet", "getNewsFeed", "follow", "postTweet", "getNewsFeed", "unfollow", "getNewsFeed"]
[[], [1, 5], [1], [1, 2], [2, 6], [1], [1, 2], [1]]
输出
[null, null, [5], null, null, [6, 5], null, [5]]

解释
Twitter twitter = new Twitter();
twitter.postTweet(1, 5); // 用户 1 发送了一条新推文 (用户 id = 1, 推文 id = 5)
twitter.getNewsFeed(1);  // 用户 1 的获取推文应当返回一个列表,其中包含一个 id 为 5 的推文
twitter.follow(1, 2);    // 用户 1 关注了用户 2
twitter.postTweet(2, 6); // 用户 2 发送了一个新推文 (推文 id = 6)
twitter.getNewsFeed(1);  // 用户 1 的获取推文应当返回一个列表,其中包含两个推文,id 分别为 -> [6, 5] 。推文 id 6 应当在推文 id 5 之前,因为它是在 5 之后发送的
twitter.unfollow(1, 2);  // 用户 1 取消关注了用户 2
twitter.getNewsFeed(1);  // 用户 1 获取推文应当返回一个列表,其中包含一个 id 为 5 的推文。因为用户 1 已经不再关注用户 2

 

提示:

  • 1 <= userId, followerId, followeeId <= 500
  • 0 <= tweetId <= 104
  • 所有推特的 ID 都互不相同
  • postTweetgetNewsFeedfollowunfollow 方法最多调用 3 * 104

方法一:哈希表

我们使用一下三种数据来记录题目所需各种信息:

  • unordered_map<int, unordered_set<int>> followList来记录关注信息。如果followList[1] = {2, 5, 7},就说明用户1关注了用户257
  • unordered_map<int, vector<pair<int, int>>> posts来记录发文信息。如果posts[1] = {{1, 2}, {5, 3}, {6, 7}},就说明用户1发了三篇文,文章id分别是153,这三篇文章的发文时间分别是237
  • int th来记录每次发文的“时间”。

那么,对于题目要求的4中操作:

  • 用户a发的文章tweetId:posts[a].push_back({tweedId, th++})
  • 用户a关注用户b:followList[a].insert(b)
  • 用户a取关用户b:followList[a].erase(b)
  • 调取用户a关注列表中的最近10篇文章:
    这个相比起来稍微复杂一些。
    • 首先这道题没有“用户注册”这一操作,也就是说对于一个用户你无法直到他是否是第一次出现(可能没有出现过上来就直接调用了)。因此,在获取用户a关注列表中最近10篇文章时,需首先判断a是否以及添加到了自己的“关注列表”里面。如果还没有,那么把a加入自己的关注列表。(如果题目有“用户注册”这一操作,就可以在用户注册的时候将自己添加到自己的关注列表中)
    • 其次,因为这道题只需要调取10篇推文,所以其实不需要排序,每次调取一篇,进行10次操作即可。推文要调取“发布时间尽可能大”的,因此操作时,记录“上次调取的推文的时间”,然后遍历关注列表中每一位用户的所有推文,记录“时间上 小于上一篇推文 的 最大推文”,即为这次操作要获取的推文。

总体码量不大。

  • 时间复杂度:单次发文、关注、取关:$O(1)$;单次获取10篇关注列表的推文:$O(C\times F\times N)$,其中$C$是常数$10$,$F$是这位用户关注了多少人,$N$是这位用户关注的人中平均每人的发文量。
  • 空间复杂度$O(S+U)$,其中$S$是总发文量,$U$是总关注量

获取10篇推文时,可以优化的是:

  1. 最多获取10篇推文,因此发布过早的推文可以直接删除
  2. 对于某位用户,其推文是有序的。因此不需要遍历所有用户的全部推文10次,而是可以在关注列表中,每次选取“最后一篇推文”中,发布最晚的那一篇。这个方法类似于“合并升序链表

AC代码

C++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
typedef pair<int, int> pii;  // <twitterId, th>

class Twitter {
private:
unordered_map<int, unordered_set<int>> followList;
unordered_map<int, vector<pii>> posts;
int th;
public:
Twitter() {
th = 0;
}

void postTweet(int userId, int tweetId) {
posts[userId].push_back({tweetId, th++});
}

vector<int> getNewsFeed(int userId) {
if (!followList[userId].count(userId))
followList[userId].insert(userId);
vector<int> ans;
int lastTh = INT_MAX;
for (int i = 0; i < 10; i++) {
int maxTh = -1;
int idOfTheMaxTh;
for (int followeeId : followList[userId]) {
for (auto[twitterId, twitterTh] : posts[followeeId]) {
if (twitterTh >= lastTh)
continue;
if (twitterTh > maxTh) {
maxTh = twitterTh;
idOfTheMaxTh = twitterId;
}
}
}
if (maxTh == -1)
break;
lastTh = maxTh;
ans.push_back(idOfTheMaxTh);
}
return ans;
}

void follow(int followerId, int followeeId) {
followList[followerId].insert(followeeId);
}

void unfollow(int followerId, int followeeId) {
followList[followerId].erase(followeeId);
}
};

同步发文于CSDN,原创不易,转载请附上原文链接哦~
Tisfy:https://letmefly.blog.csdn.net/article/details/127159464


355.设计推特
https://blog.letmefly.xyz/2022/10/04/LeetCode 0355.设计推特/
作者
Tisfy
发布于
2022年10月4日
许可协议