[LeetCode/LintCode] Design Twitter/Mini Twitter

Design Twitter

Note

創建兩個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

Solution

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);        
    }
}

Mini Twitter

Problem

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.設計

Example

postTweet(1, "LintCode is Good!!!")
-->> 1
getNewsFeed(1)
-->> [1]
getTimeline(1)
-->> [1]
follow(2, 1)
getNewsFeed(2)
-->> [1]
unfollow(2, 1)
getNewsFeed(2)
-->> []

Note

設計一個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: 包含Tweetorder,構造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>>的數據結構。

這樣就很清楚了,咱們須要創建一個包含TweetOrderNode同時聲明兩個全局變量users_tweetsfriends

而後考慮要求實現的功能。
發推,要用給出的Tweet.create(user_id, tweet_text)函數,而後將集成Tweet和orderNode和對應的user_id放入users_tweets
時間線,須要從users_tweets中按照Order取出對應user_id的後10Node,再取出Node.tweet放入結果數組List<Tweet>,注意tweet的大小寫;
新鮮事,須要查看user_id的好友列表,將包括本身的每一個人的後10Node放入List<Node> temp,再對temp中全部Node進行排序,取出前10Node。這裏發現order不是對應於單個user_id的,而是對應users_tweets中的全部Node
因此,order也要聲明爲全局變量。
繼續,關注好友,添加或查找from_user_id做爲friends中的key值,而後對這個key值對應的value,也就是Map<Integer, Boolean>,添加to_user_idtrue的pair。
取關好友,和關注好友相同的操做,先從friendgetfrom_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()的初始化。
結束~

Solution

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);
    }
}
相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息