Leetcode 355. 设计推特(中等) 多路归并&面向对象设计
题目:
Twitter 和微博功能差不多,我们主要要实现这样几个 API:
class Twitter { /** user 发表一条 tweet 动态 */ public void postTweet(int userId, int tweetId) {} /** 返回该 user 关注的人(包括他自己)最近的动态 id, 最多 10 条,而且这些动态必须按从新到旧的时间线顺序排列。*/ public List<Integer> getNewsFeed(int userId) {} /** follower 关注 followee,如果 Id 不存在则新建 */ public void follow(int followerId, int followeeId) {} /** follower 取关 followee,如果 Id 不存在则什么都不做 */ public void unfollow(int followerId, int followeeId) {}
举个具体的例子,方便大家理解 API 的具体用法:
Twitter twitter = new Twitter(); twitter.postTweet(1, 5); // 用户 1 发送了一条新推文 5 twitter.getNewsFeed(1); // return [5],因为自己是关注自己的 twitter.follow(1, 2); // 用户 1 关注了用户 2 twitter.postTweet(2, 6); // 用户2发送了一个新推文 (id = 6) twitter.getNewsFeed(1); // return [6, 5] // 解释:用户 1 关注了自己和用户 2,所以返回他们的最近推文 // 而且 6 必须在 5 之前,因为 6 是最近发送的 twitter.unfollow(1, 2); // 用户 1 取消关注了用户 2 twitter.getNewsFeed(1); // return [5]
思路:
这几个 API 中大部分都很好实现,最核心的功能难点应该是 getNewsFeed
,因为返回的结果必须在时间上有序,但问题是用户的关注是动态变化的,怎么办?
这里就涉及到算法了:如果我们把每个用户各自的推文存储在链表里,每个链表节点存储文章 id 和一个时间戳 time(记录发帖时间以便比较),而且这个链表是按 time 有序的,那么如果某个用户关注了 k 个用户,我们就可以用合并 k 个有序链表的算法合并出有序的推文列表,正确地 getNewsFeed
了
我们使用map<useId,User>存储用户,并且设计结构体Tweet用于保存发送推文列表,用User对象实现用户相关的功能
在User中,用unordered_set保存关注的follow,方便快速删除。用Tweet* head链表保存发送的推文。每个Tweet保存time记录发送时间,time大的在前。
int timestamp; class Twitter { public: // 推文 struct Tweet{ // 发送时间 int time; int id; Tweet* next; Tweet(int i,int t){ id=i; time=t; next=nullptr; } //用于生产大顶堆 bool operator<(const Tweet& t) const{ return time<t.time; } }; // 用户 class User{ public: User(int u){ userId=u; head=nullptr; // 首先follow一下自己,便于在归并的时候遍历 follow(u); } // 发送推文,并将time大的放在最前边 void postTweet(int userId, int tweetId){ Tweet* t=new Tweet(tweetId,timestamp++); t->next=head; head=t; } void follow(int id){ followed.emplace(id); } void unfollow(int id){ followed.erase(id); } int userId; // 保存follow的用户id unordered_set<int> followed; // 本人发送过的推文,按time从大到小排列 Tweet* head; }; public: Twitter() { timestamp=0; } void postTweet(int userId, int tweetId) { User* user; //如果user存在就取出,不存在就创建 if(user_map.count(userId)>0){ user=user_map[userId]; }else{ user=new User(userId); user_map.insert(make_pair(userId,user)); } user->postTweet(userId,tweetId); } vector<int> getNewsFeed(int userId) { vector<int> ret; User* user; //用户不存在就返回空结果 if(user_map.count(userId)>0){ user=user_map[userId]; }else{ return ret; } // 大顶堆,按照time大小排列 priority_queue<Tweet*> pq; //将followed列表中tweet链表放入,注意有可能有用户没有发过推文即head为NULL for(auto it=user->followed.begin(); it!=user->followed.end(); ++it){ User* f=user_map[*it]; if(f->head==nullptr){ continue; } pq.push(f->head); } // 合并链表,当pq.size==0时退出 while(!pq.empty()){ Tweet* t=pq.top(); pq.pop(); ret.push_back(t->id); //如果tweet链表空了,pq的size就-1 if(t->next!=nullptr){ pq.push(t->next); } //结果满10就退出 if(ret.size()==10){ break; } } return ret; } void follow(int followerId, int followeeId) { User* follower; if(user_map.count(followerId)>0){ follower=user_map[followerId]; }else{ follower=new User(followerId); user_map.insert(make_pair(followerId,follower)); } User* followee; if(user_map.count(followeeId)>0){ followee=user_map[followeeId]; }else{ followee=new User(followeeId); user_map.insert(make_pair(followeeId,followee)); } follower->follow(followeeId); } void unfollow(int followerId, int followeeId) { User* follower; if(user_map.count(followerId)>0){ follower=user_map[followerId]; }else{ follower=new User(followerId); user_map.insert(make_pair(followerId,follower)); } User* followee; if(user_map.count(followeeId)>0){ followee=user_map[followeeId]; }else{ followee=new User(followeeId); user_map.insert(make_pair(followeeId,followee)); } follower->unfollow(followeeId); } unordered_map<int,User*> user_map; };
联系方式:emhhbmdfbGlhbmcxOTkxQDEyNi5jb20=