golang刷leetcode 经典(3) 设计推特

2022-08-02 17:30:28 浏览数 (2)

设计一个简化版的推特(Twitter),可以让用户实现发送推文,关注/取消关注其他用户,能够看见关注人(包括自己)的最近十条推文。你的设计需要支持以下的几个功能:

代码语言:javascript复制


postTweet(userId, tweetId): 创建一条新的推文
getNewsFeed(userId): 检索最近的十条推文。每个推文都必须是由此用户关注的人或者是用户自己发出的。推文必须按照时间顺序由最近的开始排序。
follow(followerId, followeeId): 关注一个用户
unfollow(followerId, followeeId): 取消关注一个用户
示例:


Twitter twitter = new Twitter();


// 用户1发送了一条新推文 (用户id = 1, 推文id = 5).
twitter.postTweet(1, 5);


// 用户1的获取推文应当返回一个列表,其中包含一个id为5的推文.
twitter.getNewsFeed(1);


// 用户1关注了用户2.
twitter.follow(1, 2);


// 用户2发送了一个新推文 (推文id = 6).
twitter.postTweet(2, 6);


// 用户1的获取推文应当返回一个列表,其中包含两个推文,id分别为 -> [6, 5].
// 推文id6应当在推文id5之前,因为它是在5之后发送的.
twitter.getNewsFeed(1);


// 用户1取消关注了用户2.
twitter.unfollow(1, 2);


// 用户1的获取推文应当返回一个列表,其中包含一个id为5的推文.
// 因为用户1已经不再关注用户2.
twitter.getNewsFeed(1);

解题思路

动态的实现一般使用“拉模式”或者“推模式”,即用户可以看到的动态可以采用查询的时候直接计算(拉)也可以在用户的关注者发推的时候直接“推”到用户的动态列表。 本文使用“推模式”实现,如下是用到的几个数据结构: a)tweets用来存放用户发表的推文; b)feeds用来存放每个用户可以看到的动态; c)fans用来存放用户的粉丝(关注者)列表。 接下来看一下几个方法的实现逻辑: PostTweet:当用户发送一条推文时,tweets存一下该推文的id与时间,feeds把该动态append到末尾; GetNewsFeed:从末尾开始遍历feeds,返回最近的10条推文id; Follow:有用户a关注用户b,则把a放入b的fans列表,且把b的tweets推文并入a的feeds,因合并的两部分均是按时间升序排列的数组,所以避免使用常规排序算法,使用自写的merge函数可以加速合并; Unfollow:用用户a取消关注b,则将a从b的fans列表移除,还要从a的feeds中移除b的tweets。

注意几个坑

1,用户follow自己不合法

2,重复follow,unfollow

3,按时间戳排序,最好是id生成器,这里用unixnano简化,1000qps足够了

代码语言:javascript复制
type Twitter struct {
   users map[int]*User
   tweets map[int]*Tweet
}

type Tweet struct{
    ID int
    UID int
    time int64
}
type User struct{
    ID int
   followers map[int]*User
   followees map[int]*User //关注的人,推模式用,这里没有使用
   feed  []*Tweet
   tweets []*Tweet
}

func NewUser(userId int)*User{
   return &User{
            ID:userId,
            followers:make(map[int]*User),
            followees:make(map[int]*User),
         }
}

/** Initialize your data structure here. */
func Constructor() Twitter {
    return Twitter{
        users:make( map[int]*User),
        tweets:make( map[int]*Tweet),
    }
}


/** Compose a new tweet. */
func (this *Twitter) PostTweet(userId int, tweetId int)  {
    u,ok:=this.users[userId]
    if !ok{
        u=NewUser(userId)
         this.users[userId]=u
    }
    t:=Tweet{ID:tweetId,UID:userId,time: time.Now().UnixNano()}
    this.tweets[tweetId]=&t

//fmt.Println(*this,len(this.users[userId].followers))
    //push
    u.feed=append(u.feed,&t)
     u.tweets=append(u.tweets,&t)

    for _,f:=range u.followers{
        //fmt.Println(f)
        if f!=nil{
            f.feed=append(f.feed,&t)
        }
    }
    //fmt.Println(u)
}


/** 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. */
func (this *Twitter) GetNewsFeed(userId int) []int {
    var r []int
    u,ok:=this.users[userId]
    if !ok{
        return r
    }
    
    for i:=0;i<len(u.feed);i  {
       if i==10 {
           break
       } 
       r=append(r,u.feed[len(u.feed)-1-i].ID)
    }
   return r
}


/** Follower follows a followee. If the operation is invalid, it should be a no-op. */
func (this *Twitter) Follow(followerId int, followeeId int)  {
    if followerId==followeeId{
        return
    }
    f1,ok:=this.users[followerId]
    if !ok{
        f1=NewUser(followerId)
         //fmt.Println(1)
         this.users[followerId]=f1
    }
    f2,ok:=this.users[followeeId]
    if !ok{
        f2=NewUser(followeeId)
       // fmt.Println(2)
         this.users[followeeId]=f2
    }
    if _,ok:=f2.followers[followerId];ok{
       return
    }
    f2.followers[followerId]=f1
    f1.feed=this.Merge(f1.feed,f2.tweets)
    return
}

func (this *Twitter)Merge(f1,f2 []*Tweet)[]*Tweet{
    if len(f1)==0{
        return f2
    }
    if len(f2)==0{
        return f1
    }
    var f []*Tweet
    i:=0
    j:=0
    for i<len(f1) && j<len(f2){
        if f1[i].time<f2[j].time{
           f=append(f,f1[i])
           i  
        }else{
            f=append(f,f2[j])
            j  
        }
    }
    for ;i<len(f1);i  {
         f=append(f,f1[i])
    }
        for ;j<len(f2);j  {
         f=append(f,f2[j])
    }
    return f
}


/** Follower unfollows a followee. If the operation is invalid, it should be a no-op. */
func (this *Twitter) Unfollow(followerId int, followeeId int)  {
       if followerId==followeeId{
        return
    }
   f1,ok:=this.users[followerId]
   if !ok{
        return
    }
    f2,ok:=this.users[followeeId]
    if !ok{
        return
    }
    if _,ok=f2.followers[followerId];!ok{
        return
    }
    delete(f2.followers,followerId)
    var feedNew []*Tweet
    for i:=range f1.feed{
        //fmt.Println(f1.feed[i].UID,f2.ID)
        if f1.feed[i].UID!=f2.ID{
           feedNew=append(feedNew,f1.feed[i])
        }
    }
    f1.feed=feedNew
}


/**
 * Your Twitter object will be instantiated and called as such:
 * obj := Constructor();
 * obj.PostTweet(userId,tweetId);
 * param_2 := obj.GetNewsFeed(userId);
 * obj.Follow(followerId,followeeId);
 * obj.Unfollow(followerId,followeeId);
 */

0 人点赞