355. Design Twitter

2 hashMap(Map<Integer, List<Tweet>>, ) + list sort

T: O(mnlogmn)

S: O(mn)

class Twitter {

    class Tweet {
        int userId;
        int tweetId;
        int timestamp;
        Tweet(int userId,
              int tweetId,
              int timestamp) {
            
            this.userId = userId;
            this.tweetId = tweetId;
            this.timestamp = timestamp;
        }
    }
    Map<Integer, List<Tweet>> postMap;
    Map<Integer, Set<Integer>> followMap;
    int timestamp = 0;
    
    public Twitter() {
        postMap = new HashMap<>();
        followMap = new HashMap<>();
    }
    
    public void postTweet(int userId, int tweetId) {
        postMap.putIfAbsent(userId, new ArrayList<>());
        postMap.get(userId).add(new Tweet(userId, tweetId, timestamp++));
    }
    
    public List<Integer> getNewsFeed(int userId) {
        List<Tweet> all = new ArrayList<>();
        // PriorityQueue<Tweet> pq = new PriorityQueue<>((a, b) -> b.timestamp - a.timestamp);
        
        for (int targetId : followMap.getOrDefault(userId, new HashSet<>())) {
            for (Tweet tweet : postMap.getOrDefault(targetId, new ArrayList<>())) {
                // pq.offer(tweet);
                all.add(tweet);
            }
        }
        for (Tweet tweet : postMap.getOrDefault(userId, new ArrayList<>())) {
            // pq.offer(tweet);
            all.add(tweet);
        }
        Collections.sort(all, (a, b) -> b.timestamp - a.timestamp);
        List<Integer> result = new ArrayList<>();
        // int n = 0;
        // while (!pq.isEmpty() && n < 10) {
        //     result.add(pq.poll().tweetId);
        //     n++;
        // }
        int n = 0;
        while (n < 10 && n < all.size()) {
            result.add(all.get(n).tweetId);
            n++;
        }
        return result;
    }
    
    public void follow(int followerId, int followeeId) { // follower follow followee
        followMap.putIfAbsent(followerId, new HashSet<>());
        followMap.get(followerId).add(followeeId);
    }
    
    public void unfollow(int followerId, int followeeId) {
        Set<Integer> set = followMap.getOrDefault(followerId, new HashSet<>());
        if (set.contains(followeeId)) {
            set.remove(followeeId);
        }
    }
}

/**
 * Your Twitter object will be instantiated and called as such:
 * Twitter obj = new Twitter();
 * obj.postTweet(userId,tweetId);
 * List<Integer> param_2 = obj.getNewsFeed(userId);
 * obj.follow(followerId,followeeId);
 * obj.unfollow(followerId,followeeId);
 
1. use 2 hashMap 
Map<Integer, List<Tweet>> postMap;
use ArrayList save all tweets with timestamp, postTweet: O(1)

Map<Integer, Set<Integer>> followMap;
use set to save followee, in that way, unfollow is O(1)
follow is also O(1)

getNewsFeed()
use PQ, but needs to get all followee * tweets count
add all followee * tweets count: O(mn)
PQ: O(mnlogmn) 

add all tweets into PQ to only add top 10 recent tweets to result
no difference comparing with just "sort"  ...because I get all tweets here and add all into PQ

so actually this solution is using list, add all followee * tweets count, then sort
add all followee * tweets count: O(mn)
sort: O(mnlogmn)

 */

hashMap(Map<Integer, User>) + User has tweet linkedlist, + PQ

getNewsFeed()
T: O(this user's followed count + 10)...actually I think it still need to add all..

T: postTweet, follow, unfollow: O(1)

S: O(user*followed + user* tweets count)
class Twitter {
    int timestamp = 0;

    class Tweet {
        private int tweetId;
        private Tweet next;
        private int timestamp;
        Tweet(int tweetId,
              int timestamp) {
            
            this.tweetId = tweetId;
            this.next = null;
            this.timestamp = timestamp;
        }
    }
    class User {
        private int id;
        private Tweet head;
        private Set<Integer> followed;
        
        User(int userId) {
            this.id = userId;
            this.head = null;
            followed = new HashSet<>();
            follow(userId); // follow yourself
        }
        public void follow(int userId) {
            this.followed.add(userId);
        }
        public void unfollow(int userId) {
            if (followed.contains(userId) && this.id != userId) { // 不可以取消關注自己
                this.followed.remove(userId);
            }
        }
        public void post(int tweetId, int timestamp) {
            Tweet tweet = new Tweet(tweetId, timestamp);
            tweet.next = head;
            head = tweet;
        }
    }
    
    private Map<Integer, User> postMap;
    
    public Twitter() {
        postMap = new HashMap<>();
    }
    
    public void postTweet(int userId, int tweetId) {
        postMap.putIfAbsent(userId, new User(userId));
        postMap.get(userId).post(tweetId, timestamp++);
    }
    
    public List<Integer> getNewsFeed(int userId) {
        List<Integer> result = new ArrayList<>();
        if (!postMap.containsKey(userId)) {
            return result; // 根本沒這人
        }
        Set<Integer> followed = postMap.get(userId).followed;
            
        PriorityQueue<Tweet> pq = new PriorityQueue<>((a, b) -> b.timestamp - a.timestamp);
        for (int f : followed) {
            User u = postMap.get(f);
            if (u == null) {
                continue;
            }
            Tweet head = u.head;
            if (head == null) {
                continue;
            }
            pq.offer(head);
        }
        
        while (!pq.isEmpty() && result.size() < 10) {
            Tweet tweet = pq.poll();
            result.add(tweet.tweetId);
            if (tweet.next != null) {
                pq.offer(tweet.next);
            }
        }
        
        return result;
    }
    
    public void follow(int followerId, int followeeId) { // follower follow followee
        postMap.putIfAbsent(followerId, new User(followerId));
        postMap.get(followerId).follow(followeeId);
    }
    
    public void unfollow(int followerId, int followeeId) {
        if (postMap.containsKey(followerId)){
            postMap.get(followerId).unfollow(followeeId);
        }
    }
}

/**
 * Your Twitter object will be instantiated and called as such:
 * Twitter obj = new Twitter();
 * obj.postTweet(userId,tweetId);
 * List<Integer> param_2 = obj.getNewsFeed(userId);
 * obj.follow(followerId,followeeId);
 * obj.unfollow(followerId,followeeId);

getNewsFeed()
T: O(this user's followed count + 10)

T: postTweet, follow, unfollow: O(1)

S: O(user*followed + user* tweets count)

 */

still use 2 map + pq, but stop earlier, this is faster

Runtime: 10 ms, faster than 97.95% of Java online submissions

pq change to use minHeap, 當遇到 timestmap 比 heap peek 大的, 就把那個小的丟掉, 放入大的

這樣可以提早結束

最後用 linkedlist addFirst 的技巧, 達到大的在前面的效果(因為 minHeap 事先排出來最小的..

class Twitter {

    class Tweet {
        int userId;
        int tweetId;
        int timestamp;
        Tweet(int userId,
              int tweetId,
              int timestamp) {
            
            this.userId = userId;
            this.tweetId = tweetId;
            this.timestamp = timestamp;
        }
    }
    Map<Integer, List<Tweet>> postMap;
    Map<Integer, Set<Integer>> followMap;
    int timestamp = 0;
    
    public Twitter() {
        postMap = new HashMap<>();
        followMap = new HashMap<>();
    }
    
    public void postTweet(int userId, int tweetId) {
        postMap.putIfAbsent(userId, new ArrayList<>());
        postMap.get(userId).add(new Tweet(userId, tweetId, timestamp++));
    }
    
    public List<Integer> getNewsFeed(int userId) {
        PriorityQueue<Tweet> pq = new PriorityQueue<>((a, b) -> a.timestamp - b.timestamp);
        LinkedList<Integer> result = new LinkedList<>();
        
        for (int targetId : followMap.getOrDefault(userId, new HashSet<>())) {
            for (Tweet tweet : postMap.getOrDefault(targetId, new ArrayList<>())) {
                if (pq.size() < 10) {
                    pq.offer(tweet);
                } else if (tweet.timestamp > pq.peek().timestamp) {
                    pq.poll();
                    pq.offer(tweet);
                }
            }
        }
        for (Tweet tweet : postMap.getOrDefault(userId, new ArrayList<>())) {
            if (pq.size() < 10) {
                pq.offer(tweet);
            } else if (tweet.timestamp > pq.peek().timestamp) {
                pq.poll();
                pq.offer(tweet);
            }
        }
        int n = 0;
        while (!pq.isEmpty() && n < 10) {
            result.addFirst(pq.poll().tweetId);
        }

        return result;
    }
    
    public void follow(int followerId, int followeeId) { // follower follow followee
        followMap.putIfAbsent(followerId, new HashSet<>());
        followMap.get(followerId).add(followeeId);
    }
    
    public void unfollow(int followerId, int followeeId) {
        Set<Integer> set = followMap.getOrDefault(followerId, new HashSet<>());
        if (set.contains(followeeId)) {
            set.remove(followeeId);
        }
    }
}

Last updated