355 Design Twitter

ref: 355. Design Twitter

Design a simplified version of Twitter where users can post tweets, follow/unfollow another user and is able to see the 10 most recent tweets in the user's news feed. Your design should support the following methods:

  1. postTweet(userId, tweetId): Compose a new tweet.
  2. getNewsFeed(userId): Retrieve the 10 most recent tweet ids in the user's news feed. Each item in the news feed must be posted by users who the user followed or by the user herself. Tweets must be ordered from most recent to least recent.
  3. follow(followerId, followeeId): Follower follows a followee.
  4. unfollow(followerId, followeeId): Follower unfollows a followee.

Example:

Twitter twitter = new Twitter();

// User 1 posts a new tweet (id = 5).
twitter.postTweet(1, 5);

// User 1's news feed should return a list with 1 tweet id -> [5].
twitter.getNewsFeed(1);

// User 1 follows user 2.
twitter.follow(1, 2);

// User 2 posts a new tweet (id = 6).
twitter.postTweet(2, 6);

// User 1's news feed should return a list with 2 tweet ids -> [6, 5].
// Tweet id 6 should precede tweet id 5 because it is posted after tweet id 5.
twitter.getNewsFeed(1);

// User 1 unfollows user 2.
twitter.unfollow(1, 2);

// User 1's news feed should return a list with 1 tweet id -> [5],
// since user 1 is no longer following user 2.
twitter.getNewsFeed(1);

分析

自己的方法:

  • 里面包含了三个成员

Map> tweets;//map> Map> relations;//map> Map tweetsOrder;//map

tweets里面key是userId,内容是这个用户发的所有tweetId

relations是key是userId,内容是这个用户follow的所有用户

tweetOrder中的key是用户id和tweetId的拼接,value是总编号

  • 在getLastestNews和merge k sorted list差不多,用一个PriorityQueue来维护最近的tweet

  还要注意的是get最新10条的时候记得把自己的设置成自己的follower

  • unfollow的时候注意:

  1. 要检查这个关系是都存在

  2. 自己不可以unfollow自己

所以代码就是:

public class Twitter {
    Map<Integer, List<Integer>> tweets;//map<userId, list<hisTweets>>
    Map<Integer, Set<Integer>> relations;//map<follower, list<following>>
    Map<String, Integer> tweetsOrder;//map<Key==userId:tweetId, orderInTotal>

    /** Initialize your data structure here. */
    public Twitter() {
        tweets = new HashMap<>();
        relations = new HashMap<>();
        tweetsOrder = new HashMap<>();
    }

    /** Compose a new tweet. */
    public void postTweet(int userId, int tweetId) {
        List<Integer> tweetsList = tweets.get(userId);
        if(tweetsList == null) {
            tweetsList = new ArrayList<>();
        }
        tweetsList.add(0, tweetId);
        tweets.put(userId, tweetsList);
        int index = tweetsOrder.size();
        StringBuilder sb = new StringBuilder();
        sb.append(userId);
        sb.append(":");
        sb.append(tweetId);
        tweetsOrder.put(sb.toString(), index);
    }

    class TweetNode {
        int userId;
        int tweetIndex;
        int tweetId;
        int tweetOrder;
        public TweetNode(int userId, int tweetIndex) {
            this.userId = userId;
            this.tweetIndex = tweetIndex;
            this.tweetId = tweets.get(userId).get(tweetIndex);
            String key = String.valueOf(userId) + ":" + String.valueOf(this.tweetId);
            this.tweetOrder = tweetsOrder.get(key);
        }
    }

    /** Retrieve the 10 most recent tweet ids in the user's news feed. Each item in the news feed must be posted by users who the user followed or by the user herself. Tweets must be ordered from most recent to least recent. */
    public List<Integer> getNewsFeed(int userId) {
        follow(userId, userId);
        PriorityQueue<TweetNode> pq = new PriorityQueue<>(new Comparator<TweetNode>() {
            public int compare(TweetNode t1, TweetNode t2) {
                return t2.tweetOrder - t1.tweetOrder;
            }
        });
        for(int person: relations.get(userId)) {
            List<Integer> list = tweets.get(person);
            if(list != null) {
                pq.offer(new TweetNode(person, 0));
            }
        }
        int cnt = 10;
        List<Integer> res = new ArrayList<>();
        while(cnt > 0 && !pq.isEmpty()) {
            TweetNode cur = pq.poll();
            cnt--;
            res.add(cur.tweetId);
            if(cur.tweetIndex < tweets.get(cur.userId).size() - 1) {
                pq.offer(new TweetNode(cur.userId, cur.tweetIndex + 1));
            }
        }
        return res;
    }

    /** Follower follows a followee. If the operation is invalid, it should be a no-op. */
    public void follow(int followerId, int followeeId) {
        Set<Integer> following = relations.get(followerId);
        if(following == null) {
            following = new HashSet<>();
        }
        following.add(followeeId);
        relations.put(followerId, following);
    }

    /** Follower unfollows a followee. If the operation is invalid, it should be a no-op. */
    public void unfollow(int followerId, int followeeId) {
        Set<Integer> following = relations.get(followerId);
        if(following != null && followerId != followeeId && following.contains(followeeId)) {
            following.remove(followeeId);
        }
        relations.put(followerId, following);
    }
}

/**
 * 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);
 */

ref: https://discuss.leetcode.com/topic/48100/java-oo-design-with-most-efficient-function-getnewsfeed

这个设计得漂亮一些:

  1. 把user和tweet都单独建成类
  2. 用一个全局static的timestamp来给tweet编上全局的编号,方便排序,在创建一条Tweet的时候更新这个计数
  3. 在user类中加入一个Tweet的头,把一个用户的tweets设成一个linkedList,这样可以提高PriorityQueue的工作效率
  4. 根据以上的设计,属于Twitter的大类的只有两个成员了,一个是把用户id和User实例对应的map,一个全局的timestamp
public class Twitter {
    private static int timestamp = 0;
    Map<Integer, User> userMap;

    /** Initialize your data structure here. */
    public Twitter() {
        userMap = new HashMap<>();
    }

    class User {
        int userId;
        Set<Integer> following;
        Tweet tweetHead;
        public User(int userId) {
            this.userId = userId;
            following = new HashSet<>();
            following.add(userId);
            tweetHead = null;
        }

        public void post(int tweetId) {
            Tweet newTweet = new Tweet(userId, tweetId);
            newTweet.next = tweetHead;
            tweetHead = newTweet;
        }

        public void follow(int followeeId) {
            following.add(followeeId);
        }

        public void unfollow(int followeeId) {
            following.remove(followeeId);
        }
    }

    class Tweet {
        int userId;
        int tweetId;
        int time;
        Tweet next;

        public Tweet(int userId, int tweetId) {
            this.userId = userId;
            this.tweetId = tweetId;
            time = timestamp++;
            next = null;
        }
    }


    /** Compose a new tweet. */
    public void postTweet(int userId, int tweetId) {
        User user = userMap.get(userId);
        if(user == null) {
            user = new User(userId);
            userMap.put(userId, user);
        }
        user.post(tweetId);
    }

    /** Retrieve the 10 most recent tweet ids in the user's news feed. Each item in the news feed must be posted by users who the user followed or by the user herself. Tweets must be ordered from most recent to least recent. */
    public List<Integer> getNewsFeed(int userId) {
        List<Integer> res = new ArrayList<>();
        if(!userMap.containsKey(userId)) {
            return res;
        }
        PriorityQueue<Tweet> pq = new PriorityQueue<>(new Comparator<Tweet>(){
            public int compare(Tweet t1, Tweet t2) {
                return t2.time - t1.time;
            }
        });
        for(int id: userMap.get(userId).following) {
            User curUser = userMap.get(id);
            if(curUser.tweetHead != null) {
                pq.offer(curUser.tweetHead);
            }
        }
        int cnt = 10;
        while(cnt > 0 && !pq.isEmpty()) {
            Tweet cur = pq.poll();
            res.add(cur.tweetId);
            cnt--;
            if(cur.next != null) {
                pq.offer(cur.next);
            }
        }
        return res;
    }

    /** Follower follows a followee. If the operation is invalid, it should be a no-op. */
    public void follow(int followerId, int followeeId) {
        User follower = userMap.get(followerId);
        if(follower == null) {
            follower = new User(followerId);
            userMap.put(followerId, follower);
        }
        User followee = userMap.get(followeeId);
        if(followee == null) {
            followee = new User(followeeId);
            userMap.put(followeeId, followee);
        }
        follower.follow(followeeId);
    }

    /** Follower unfollows a followee. If the operation is invalid, it should be a no-op. */
    public void unfollow(int followerId, int followeeId) {
        if(userMap.containsKey(followerId) && userMap.containsKey(followeeId) && followerId != followeeId) {
            userMap.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);
 */


results matching ""

    No results matching ""