創建兩個HashMap,一個存user,一個存tweets。以及整型的時間戳timestamp。user
的k-v pair是userId-follower_set,tweets
的k-v pair是userId-tweets_linkedlist,tweets屬於Tweet類,包含time和id兩個參數。node
postTweet(userId, tweetId):
首先,對於發推的主體,user,要存放在userMap中,並且user要follow本身,因此,userMap.get(userId).add(userId);
而後,在tweets中找到key值userId,將該用戶全部推特內容放入值域的哈希表中。數組
getNewsFeed(userId):
首先創建按時間戳從大到小排列的PriorityQueue,找到userMap中的userId,filter出其中在tweet中存在的follower,把每個follower的推特鏈表放入PriorityQueue,再從PriorityQueue中取頭十條推特的id放入結果數組。數據結構
follow(followerId, followeeId):
將followeeId加入userMap中的followerId鍵值。函數
unfollow(followId, followeeId):
將followeeId從userMap中的followerId鍵值裏刪除。post
public class Twitter { Map<Integer, Set<Integer>> userMap = new HashMap<>(); Map<Integer, LinkedList<Tweet>> tweets = new HashMap<>(); int timestamp = 0; class Tweet { int time; int id; Tweet(int time, int id) { this.time = time; this.id = id; } } public void postTweet(int userId, int tweetId) { if (!userMap.containsKey(userId)) userMap.put(userId, new HashSet<>()); userMap.get(userId).add(userId); if (!tweets.containsKey(userId)) tweets.put(userId, new LinkedList<>()); tweets.get(userId).addFirst(new Tweet(timestamp++, tweetId)); } public List<Integer> getNewsFeed(int userId) { if (!userMap.containsKey(userId)) return new LinkedList<>(); PriorityQueue<Tweet> feed = new PriorityQueue<>((t1, t2) -> t2.time-t1.time); userMap.get(userId).stream().filter(f -> tweets.containsKey(f)) .forEach(f -> tweets.get(f).forEach(feed::add)); List<Integer> res = new LinkedList<>(); while (feed.size() > 0 && res.size() < 10) res.add(feed.poll().id); return res; } public void follow(int followerId, int followeeId) { if (!userMap.containsKey(followerId)) userMap.put(followerId, new HashSet<>()); userMap.get(followerId).add(followeeId); } public void unfollow(int followerId, int followeeId) { if (userMap.containsKey(followerId) && followeeId != followerId) userMap.get(followerId).remove(followeeId); } }
Implement a simple twitter. Support the following method:this
postTweet(user_id, tweet_text)
. Post a tweet.getTimeline(user_id)
. Get the given user's most recently 10 tweets posted by himself, order by timestamp from most recent to least recent.getNewsFeed(user_id)
. Get the given user's most recently 10 tweets in his news feed (posted by his friends and himself). Order by timestamp from most recent to least recent.follow(from_user_id, to_user_id)
. from_user_id followed to_user_id.unfollow(from_user_id, to_user_id)
. from_user_id unfollowed to to_user_id.設計
postTweet(1, "LintCode is Good!!!") -->> 1 getNewsFeed(1) -->> [1] getTimeline(1) -->> [1] follow(2, 1) getNewsFeed(2) -->> [1] unfollow(2, 1) getNewsFeed(2) -->> []
設計一個mini Twitter,剛放出來的幾道系統設計題裏,這道題纔算是關於設計的題目。
要實現這麼五個功能:code
發推:須要用戶ID,推特內容,該推特的時間戳; 時間線:須要用戶ID,該用戶的推特集,每條推特的時間戳,比較器; 新鮮事:須要用戶ID,好友ID集合,選定用戶羣(用戶ID和全部好友ID)的推特集(選定用戶的推特集,時間戳),比較器; 關注:須要用戶ID,被關注用戶ID,好友ID集合; 取關:須要用戶ID,被關注用戶ID,好友ID集合;
以上,根據功能的要求,肯定相關變量。而後選擇適當的數據結構:對象
已經給出的:Tweet(user_id, text, id, create()),
沒有給出可是須要的:時間戳order,帶時間戳的推特Node,推特集,全部推特集users_tweets,好友集friends,整個系統MiniTwitter,排序
而後,分析一下具體對象之間的邏輯關係:
Node: 包含Tweet
和order
,構造Node
類;
users_tweets: 包含user_id
,和對應id的推特集List<Node>
,使用Map<Integer, List<Node>>
的數據結構;
friends: 包含user_id
,每一個id對應一個好友集Map<Integer, Boolean>
(即Map<user-id, isFriend>()
),使用Map<Integer, Map<Integer, Boolean>>
的數據結構。
這樣就很清楚了,咱們須要創建一個包含Tweet
和Order
的Node
類,同時聲明兩個全局變量users_tweets
和friends
。
而後考慮要求實現的功能。
發推,要用給出的Tweet.create(user_id, tweet_text)
函數,而後將集成Tweet和order的Node
和對應的user_id
放入users_tweets
;
時間線,須要從users_tweets
中按照Order
取出對應user_id
的後10
個Node
,再取出Node.tweet
放入結果數組List<Tweet>
,注意tweet
的大小寫;
新鮮事,須要查看user_id
的好友列表,將包括本身的每一個人的後10
個Node
放入List<Node> temp
,再對temp
中全部Node
進行排序,取出前10
個Node
。這裏發現order
不是對應於單個user_id
的,而是對應users_tweets
中的全部Node
。
因此,order
也要聲明爲全局變量。
繼續,關注好友,添加或查找from_user_id
做爲friends
中的key值,而後對這個key值對應的value,也就是Map<Integer, Boolean>
,添加to_user_id
和true
的pair。
取關好友,和關注好友相同的操做,先從friend
中get
的from_user_id
的key值,再remove
對應Map中to_user_id
的pair便可。
下面討論以上功能實現須要增長的細節。首先,取出前十個Node
,取出後十個Node
,須要創建兩個函數getFirstTen()
和getLastTen()
,對List<Node> temp
進行操做。因爲取出子數組的順序依然相對不變,temp.subList(start, end)
返回的十個Node
須要被從大到小排序,以知足most recent的要求(order
從大到小),咱們須要構造新的Comparator SortByOrder
,對Node
類型的數組排序。注意在Comparator
的實現上,return 1
表明須要交換,return -1
表明不須要交換。
最後,在功能函數的前面,進行MiniTweeter()
的初始化。
結束~
public class MiniTwitter { class Node { public int order; public Tweet tweet; public Node(int order, Tweet tweet) { this.order = order; this.tweet = tweet; } } class SortByOrder implements Comparator { public int compare(Object obj1,Object obj2) { Node node1 = (Node) obj1; Node node2 = (Node) obj2; if (node1.order < node2.order) return 1; else return -1; } } private Map<Integer, Map<Integer, Boolean>> friends; private Map<Integer, List<Node>> users_tweets; private int order; public List<Node> getLastTen(List<Node> temp) { int last = 10; if (temp.size() < 10) last = temp.size(); return temp.subList(temp.size() - last, temp.size()); } public List<Node> getFirstTen(List<Node> temp) { int last = 10; if (temp.size() < 10) last = temp.size(); return temp.subList(0, last); } public MiniTwitter() { // initialize your data structure here. this.friends = new HashMap<Integer, Map<Integer, Boolean>>(); this.users_tweets = new HashMap<Integer, List<Node>>(); this.order = 0; } // @param user_id an integer // @param tweet a string // return a tweet public Tweet postTweet(int user_id, String text) { Tweet tweet = Tweet.create(user_id, text); if (!users_tweets.containsKey(user_id)) users_tweets.put(user_id, new ArrayList<Node>()); order += 1; users_tweets.get(user_id).add(new Node(order, tweet)); return tweet; } // @param user_id an integer // return a list of 10 new feeds recently // and sort by timeline public List<Tweet> getNewsFeed(int user_id) { List<Node> temp = new ArrayList<Node>(); if (users_tweets.containsKey(user_id)) temp.addAll(getLastTen(users_tweets.get(user_id))); if (friends.containsKey(user_id)) { for (Map.Entry<Integer, Boolean> entry : friends.get(user_id).entrySet()) { int user = entry.getKey(); if (users_tweets.containsKey(user)) temp.addAll(getLastTen(users_tweets.get(user))); } } Collections.sort(temp, new SortByOrder()); List<Tweet> res = new ArrayList<Tweet>(); temp = getFirstTen(temp); for (Node node : temp) { res.add(node.tweet); } return res; } // @param user_id an integer // return a list of 10 new posts recently // and sort by timeline public List<Tweet> getTimeline(int user_id) { List<Node> temp = new ArrayList<Node>(); if (users_tweets.containsKey(user_id)) temp.addAll(getLastTen(users_tweets.get(user_id))); Collections.sort(temp, new SortByOrder()); List<Tweet> res = new ArrayList<Tweet>(); temp = getFirstTen(temp); for (Node node : temp) res.add(node.tweet); return res; } // @param from_user_id an integer // @param to_user_id an integer // from user_id follows to_user_id public void follow(int from_user_id, int to_user_id) { if (!friends.containsKey(from_user_id)) friends.put(from_user_id, new HashMap<Integer, Boolean>()); friends.get(from_user_id).put(to_user_id, true); } // @param from_user_id an integer // @param to_user_id an integer // from user_id unfollows to_user_id public void unfollow(int from_user_id, int to_user_id) { if (friends.containsKey(from_user_id)) friends.get(from_user_id).remove(to_user_id); } }