***355. 设计推特——数据结构
题目
设计一个简化版的推特( Twitter) ,可以让用户实现发送推文,关注/取消关注其他用户,能够看见关注人(包括自己)的最近 10 条推文。
实现 Twitter 类:
Twitter( ) 初始化简易版推特对象
void postTweet( int userId, int tweetId) 根据给定的 tweetId 和 userId 创建一条新推文。每次调用此函数都会使用一个不同的 tweetId 。
List< Integer> getNewsFeed( int userId) 检索当前用户新闻推送中最近 10 条推文的 ID 。新闻推送中的每一项都必须是由用户关注的人或者是用户自己发布的推文。推文必须 按照时间顺序由最近到最远排序 。
void follow( int followerId, int followeeId) ID 为 followerId 的用户开始关注 ID 为 followeeId 的用户。
void unfollow( int followerId, int followeeId) ID 为 followerId 的用户不再关注 ID 为 followeeId 的用户。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/design-twitter
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
class Twitter {
struct Node {
// 哈希表存储关注人的 Id
unordered_set<int> followee;
// 用链表存储 tweetId
list<int> tweet;
};
// getNewsFeed 检索的推文的上限以及 tweetId 的时间戳
int recentMax, time;
// tweetId 对应发送的时间
unordered_map<int, int> tweetTime;
// 每个用户存储的信息
unordered_map<int, Node> user;
public:
Twitter() {
time = 0;
recentMax = 10;
user.clear();
}
// 初始化
void init(int userId) {
user[userId].followee.clear();
user[userId].tweet.clear();
}
void postTweet(int userId, int tweetId) {
if (user.find(userId) == user.end()) {
init(userId);
}
// 达到限制,剔除链表末尾元素
if (user[userId].tweet.size() == recentMax) {
user[userId].tweet.pop_back();
}
user[userId].tweet.push_front(tweetId);
tweetTime[tweetId] = ++time;
}
vector<int> getNewsFeed(int userId) {
vector<int> ans; ans.clear();
for (list<int>::iterator it = user[userId].tweet.begin(); it != user[userId].tweet.end(); ++it) {
ans.emplace_back(*it);
}
for (int followeeId: user[userId].followee) {
if (followeeId == userId) continue; // 可能出现自己关注自己的情况
vector<int> res; res.clear();
list<int>::iterator it = user[followeeId].tweet.begin();
int i = 0;
// 线性归并
while (i < (int)ans.size() && it != user[followeeId].tweet.end()) {
if (tweetTime[(*it)] > tweetTime[ans[i]]) {
res.emplace_back(*it);
++it;
} else {
res.emplace_back(ans[i]);
++i;
}
// 已经找到这两个链表合起来后最近的 recentMax 条推文
if ((int)res.size() == recentMax) break;
}
for (; i < (int)ans.size() && (int)res.size() < recentMax; ++i) res.emplace_back(ans[i]);
for (; it != user[followeeId].tweet.end() && (int)res.size() < recentMax; ++it) res.emplace_back(*it);
ans.assign(res.begin(),res.end());
}
return ans;
}
void follow(int followerId, int followeeId) {
if (user.find(followerId) == user.end()) {
init(followerId);
}
if (user.find(followeeId) == user.end()) {
init(followeeId);
}
user[followerId].followee.insert(followeeId);
}
void unfollow(int followerId, int followeeId) {
user[followerId].followee.erase(followeeId);
}
};
作者:LeetCode-Solution
链接:https://leetcode.cn/problems/design-twitter/solution/she-ji-tui-te-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
1472. 设计浏览器历史记录–list
题目
你有一个只支持单个标签页的 浏览器 ,最开始你浏览的网页是 homepage ,你可以访问其他的网站 url ,也可以在浏览历史中后退 steps 步或前进 steps 步。
请你实现 BrowserHistory 类:
BrowserHistory( string homepage) ,用 homepage 初始化浏览器类。
void visit( string url) 从当前页跳转访问 url 对应的页面 。执行此操作会把浏览历史前进的记录全部删除。
string back( int steps) 在浏览历史中后退 steps 步。如果你只能在浏览历史中后退至多 x 步且 steps > x ,那么你只后退 x 步。请返回后退 至多 steps 步以后的 url 。
string forward( int steps) 在浏览历史中前进 steps 步。如果你只能在浏览历史中前进至多 x 步且 steps > x ,那么你只前进 x 步。请返回前进 至多 steps步以后的 url 。
示例:
输入:
[ "BrowserHistory" ,"visit" ,"visit" ,"visit" ,"back" ,"back" ,"forward" ,"visit" ,"forward" ,"back" ,"back" ]
[ [ "leetcode.com" ] ,[ "google.com" ] ,[ "facebook.com" ] ,[ "youtube.com" ] ,[ 1 ] ,[ 1 ] ,[ 1 ] ,[ "linkedin.com" ] ,[ 2 ] ,[ 2 ] ,[ 7 ] ]
输出:
[ null,null,null,null,"facebook.com" ,"google.com" ,"facebook.com" ,null,"linkedin.com" ,"google.com" ,"leetcode.com" ]
解释:
BrowserHistory browserHistory = new BrowserHistory( "leetcode.com" ) ;
browserHistory.visit( "google.com" ) ; // 你原本在浏览 "leetcode.com" 。访问 "google.com"
browserHistory.visit( "facebook.com" ) ; // 你原本在浏览 "google.com" 。访问 "facebook.com"
browserHistory.visit( "youtube.com" ) ; // 你原本在浏览 "facebook.com" 。访问 "youtube.com"
browserHistory.back( 1 ) ; // 你原本在浏览 "youtube.com" ,后退到 "facebook.com" 并返回 "facebook.com"
browserHistory.back( 1 ) ; // 你原本在浏览 "facebook.com" ,后退到 "google.com" 并返回 "google.com"
browserHistory.forward( 1 ) ; // 你原本在浏览 "google.com" ,前进到 "facebook.com" 并返回 "facebook.com"
browserHistory.visit( "linkedin.com" ) ; // 你原本在浏览 "facebook.com" 。 访问 "linkedin.com"
browserHistory.forward( 2 ) ; // 你原本在浏览 "linkedin.com" ,你无法前进任何步数。
browserHistory.back( 2 ) ; // 你原本在浏览 "linkedin.com" ,后退两步依次先到 "facebook.com" ,然后到 "google.com" ,并返回 "google.com"
browserHistory.back( 7 ) ; // 你原本在浏览 "google.com" , 你只能后退一步到 "leetcode.com" ,并返回 "leetcode.com"
提示:
1 <= homepage.length <= 20
1 <= url.length <= 20
1 <= steps <= 100
homepage 和 url 都只包含 '.' 或者小写英文字母。
最多调用 5000 次 visit, back 和 forward 函数。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/design-browser-history
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
class BrowserHistory {
public:
list<string> li;
list<string>::iterator it;
BrowserHistory(string homepage) {
li.emplace_back(homepage);
it = li.begin();
}
// 执行删除操作
void visit(string url) {
auto iter = next(it, 1);
for (; iter != li.end();) {
iter = li.erase(iter);
}
li.emplace_back(url);
it = next(it, 1);
}
string back(int steps) {
for (int i = 0; i < steps; i++) {
if (it == li.begin()) {
return *it;
}
it = prev(it, 1);
}
return *it;
}
string forward(int steps) {
for (int i = 0; i < steps; i++) {
if (li.end() == next(it, 1)) {
return *it;
}
it = next(it, 1);
}
return *it;
}
};
/**
* Your BrowserHistory object will be instantiated and called as such:
* BrowserHistory* obj = new BrowserHistory(homepage);
* obj->visit(url);
* string param_2 = obj->back(steps);
* string param_3 = obj->forward(steps);
*/
1244. 力扣排行榜——数据结构
题目
新一轮的「力扣杯」编程大赛即将启动,为了动态显示参赛者的得分数据,需要设计一个排行榜 Leaderboard。
请你帮忙来设计这个 Leaderboard 类,使得它有如下 3 个函数:
addScore( playerId, score) :
假如参赛者已经在排行榜上,就给他的当前得分增加 score 点分值并更新排行。
假如该参赛者不在排行榜上,就把他添加到榜单上,并且将分数设置为 score。
top( K) :返回前 K 名参赛者的 得分总和。
reset( playerId) :将指定参赛者的成绩清零(换句话说,将其从排行榜中删除)。题目保证在调用此函数前,该参赛者已有成绩,并且在榜单上。
请注意,在初始状态下,排行榜是空的。
示例 1 :
输入:
[ "Leaderboard" ,"addScore" ,"addScore" ,"addScore" ,"addScore" ,"addScore" ,"top" ,"reset" ,"reset" ,"addScore" ,"top" ]
[ [ ] ,[ 1,73 ] ,[ 2,56 ] ,[ 3,39 ] ,[ 4,51 ] ,[ 5,4 ] ,[ 1 ] ,[ 1 ] ,[ 2 ] ,[ 2,51 ] ,[ 3 ] ]
输出:
[ null,null,null,null,null,null,73,null,null,null,141]
解释:
Leaderboard leaderboard = new Leaderboard ( ) ;
leaderboard.addScore( 1,73 ) ; // leaderboard = [ [ 1,73 ] ] ;
leaderboard.addScore( 2,56 ) ; // leaderboard = [ [ 1,73 ] ,[ 2,56 ] ] ;
leaderboard.addScore( 3,39 ) ; // leaderboard = [ [ 1,73 ] ,[ 2,56 ] ,[ 3,39 ] ] ;
leaderboard.addScore( 4,51 ) ; // leaderboard = [ [ 1,73 ] ,[ 2,56 ] ,[ 3,39 ] ,[ 4,51 ] ] ;
leaderboard.addScore( 5,4 ) ; // leaderboard = [ [ 1,73 ] ,[ 2,56 ] ,[ 3,39 ] ,[ 4,51 ] ,[ 5,4 ] ] ;
leaderboard.top( 1 ) ; // returns 73 ;
leaderboard.reset( 1 ) ; // leaderboard = [ [ 2,56 ] ,[ 3,39 ] ,[ 4,51 ] ,[ 5,4 ] ] ;
leaderboard.reset( 2 ) ; // leaderboard = [ [ 3,39 ] ,[ 4,51 ] ,[ 5,4 ] ] ;
leaderboard.addScore( 2,51 ) ; // leaderboard = [ [ 2,51 ] ,[ 3,39 ] ,[ 4,51 ] ,[ 5,4 ] ] ;
leaderboard.top( 3 ) ; // returns 141 = 51 + 51 + 39 ;
提示:
1 <= playerId, K <= 10000
题目保证 K 小于或等于当前参赛者的数量
1 <= score <= 100
最多进行 1000 次函数调用
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/design-a-leaderboard
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
class Leaderboard {
public:
/*
**注意点:**
1. 学到了insert的返回函数是迭代器;
2. 不能修改map中的key值;只能够删除后再重新插入;
*/
multiset<int> se; // score
unordered_map<int, multiset<int>::iterator> mp; // <id, score>
Leaderboard() {
}
void addScore(int playerId, int score) {
int t = 0;
auto iter = mp.find(playerId);
if (iter != mp.end()) {
t = *(iter->second);
reset(playerId);
}
auto nit = se.insert(t + score);
mp[playerId] = nit;
}
int top(int K) {
int res = 0;
for (auto it = se.rbegin(); it != se.rend() && K > 0; it++, K--) {
res += *it;
}
return res;
}
void reset(int playerId) {
auto it = mp[playerId];
se.erase(it);
mp.erase(playerId);
}
};
/**
* Your Leaderboard object will be instantiated and called as such:
* Leaderboard* obj = new Leaderboard();
* obj->addScore(playerId,score);
* int param_2 = obj->top(K);
* obj->reset(playerId);
*/
2034. 股票价格波动-multiset
题目
给你一支股票价格的数据流。数据流中每一条记录包含一个 时间戳 和该时间点股票对应的 价格 。
不巧的是,由于股票市场内在的波动性,股票价格记录可能不是按时间顺序到来的。某些情况下,有的记录可能是错的。如果两个有相同时间戳的记录出现在数据流中,前一条记录视为错误记录,后出现的记录 更正 前一条错误的记录。
请你设计一个算法,实现:
更新 股票在某一时间戳的股票价格,如果有之前同一时间戳的价格,这一操作将 更正 之前的错误价格。
找到当前记录里 最新股票价格 。最新股票价格 定义为时间戳最晚的股票价格。
找到当前记录里股票的 最高价格 。
找到当前记录里股票的 最低价格 。
请你实现 StockPrice 类:
StockPrice() 初始化对象,当前无股票价格记录。
void update(int timestamp, int price) 在时间点 timestamp 更新股票价格为 price 。
int current() 返回股票 最新价格 。
int maximum() 返回股票 最高价格 。
int minimum() 返回股票 最低价格 。
示例 1:
输入:
["StockPrice", "update", "update", "current", "maximum", "update", "maximum", "update", "minimum"]
[[], [1, 10], [2, 5], [], [], [1, 3], [], [4, 2], []]
输出:
[null, null, null, 5, 10, null, 5, null, 2]
解释:
StockPrice stockPrice = new StockPrice();
stockPrice.update(1, 10); // 时间戳为 [1] ,对应的股票价格为 [10] 。
stockPrice.update(2, 5); // 时间戳为 [1,2] ,对应的股票价格为 [10,5] 。
stockPrice.current(); // 返回 5 ,最新时间戳为 2 ,对应价格为 5 。
stockPrice.maximum(); // 返回 10 ,最高价格的时间戳为 1 ,价格为 10 。
stockPrice.update(1, 3); // 之前时间戳为 1 的价格错误,价格更新为 3 。
// 时间戳为 [1,2] ,对应股票价格为 [3,5] 。
stockPrice.maximum(); // 返回 5 ,更正后最高价格为 5 。
stockPrice.update(4, 2); // 时间戳为 [1,2,4] ,对应价格为 [3,5,2] 。
stockPrice.minimum(); // 返回 2 ,最低价格时间戳为 4 ,价格为 2 。
提示:
1 <= timestamp, price <= 109
update,current,maximum 和 minimum 总 调用次数不超过 105 。
current,maximum 和 minimum 被调用时,update 操作 至少 已经被调用过 一次 。
思路
class StockPrice {
public:
map<int, int> mp; // <timestamp, price>
multiset<int> s; // price
StockPrice() {
}
void update(int timestamp, int price) {
auto it = mp.find(timestamp);
if (it != mp.end()) {
auto iter = s.find(it->second);
s.erase(iter);
}
mp[timestamp] = price;
s.emplace(price);
}
int current() {
//return mp[currentTime];
return mp.rbegin()->second;
}
int maximum() {
return *s.rbegin();
}
int minimum() {
return *s.begin();
}
};
/**
* Your StockPrice object will be instantiated and called as such:
* StockPrice* obj = new StockPrice();
* obj->update(timestamp,price);
* int param_2 = obj->current();
* int param_3 = obj->maximum();
* int param_4 = obj->minimum();
*/