1.LeetCode:895. 最大频率栈
设计一个类似堆栈的数据结构,将元素推入堆栈,并从堆栈中弹出出现频率最高的元素。
实现 FreqStack 类:
FreqStack() 构造一个空的堆栈。
void push(int val) 将一个整数 val 压入栈顶。
int pop() 删除并返回堆栈中出现频率最高的元素。
如果出现频率最高的元素不只一个,则移除并返回最接近栈顶的元素。
示例 1:
输入:
[“FreqStack”,“push”,“push”,“push”,“push”,“push”,“push”,“pop”,“pop”,“pop”,“pop”],
[[],[5],[7],[5],[7],[4],[5],[],[],[],[]]
输出:[null,null,null,null,null,null,null,5,7,5,4]
提示:
0 <= val <= 1e9
push 和 pop 的操作数不大于 2 * 1e4。
输入保证在调用 pop 之前堆栈中至少有一个元素。
这里需要准备两个表,一张用来统计所有元素出现的频率 cnt,另一张用栈来统计某个频率的所有数字 stk。当我们要加入某个元素的时候,对于cnt表直接将value加一即可,对于stk表,对其频率的栈加入该元素即可,并更新最大频率。
对于pop操作也很简单,先在出现频率最大的栈中删除该元素,随后其频率减减(该数字其他出现频率的栈中无需操作,因为他们也是按照先后顺序加入的),如果删除之后出现频率最大的栈中为空,最大直减一。
class FreqStack {
unordered_map<int,int> s;
int max=-100000000;
map<int,stack<int>> stk;
public:
FreqStack() {
}
void push(int val) {
s[val]++;
if(s[val]>max){
max=s[val];
}
stk[s[val]].push(val);
}
int pop() {
int x=stk[max].top();
stk[max].pop();
s[x]--;
if(!stk[max].size()){
max--;
}
return x;
}
};
2.LeetCode:732. 我的日程安排表 III
当 k 个日程安排有一些时间上的交叉时(例如 k 个日程安排都在同一时间内),就会产生 k 次预订。
给你一些日程安排 [start, end) ,请你在每个日程安排添加后,返回一个整数 k ,表示所有先前日程安排会产生的最大 k 次预订。
实现一个 MyCalendarThree 类来存放你的日程安排,你可以一直添加新的日程安排。
MyCalendarThree() 初始化对象。
int book(int start, int end) 返回一个整数 k ,表示日历中存在的 k 次预订的最大值。
示例:
输入:
[“MyCalendarThree”, “book”, “book”, “book”, “book”, “book”, “book”]
[[], [10, 20], [50, 60], [10, 40], [5, 15], [5, 10], [25, 55]]
输出:
[null, 1, 1, 2, 3, 3, 3]
提示:
0 <= start < end <= 1e9
每个测试用例,调用 book 函数最多不超过 400次
这道题就很简单了,定义一张表,key为区间左端点,val为区间右端点,加入某个区间[l,r)就相当于在这个范围内都对数组元素进行了+1操作,这里我们利用差分思想最后再求前缀和并维护最大次数即可。
class MyCalendarThree {
map<int,int> cnt;
public:
MyCalendarThree() {
cnt.clear();
}
int book(int start, int end) {
cnt[start]++;
cnt[end]--;
int sum=0,maxv=0;
for(auto i:cnt){
sum+=i.second;
maxv=max(sum,maxv);
}
return maxv;
}
};
3.LeetCode:352. 将数据流变为多个不相交区间
给你一个由非负整数 a1, a2, …, an 组成的数据流输入,请你将到目前为止看到的数字总结为不相交的区间列表。
实现 SummaryRanges 类:
SummaryRanges() 使用一个空数据流初始化对象。
void addNum(int val) 向数据流中加入整数 val 。
int[][] getIntervals() 以不相交区间 [starti, endi] 的列表形式返回对数据流中整数的总结。
示例:
输入:
[“SummaryRanges”, “addNum”, “getIntervals”, “addNum”, “getIntervals”, “addNum”, “getIntervals”, “addNum”, “getIntervals”, “addNum”, “getIntervals”]
[[], [1], [], [3], [], [7], [], [2], [], [6], []]
输出:
[null, null, [[1, 1]], null, [[1, 1], [3, 3]], null, [[1, 1], [3, 3], [7, 7]], null, [[1, 3], [7, 7]], null, [[1, 3], [6, 7]]]
提示:
0 <= val <= 1e4
最多调用 addNum 和 getIntervals 方法 3 * 1e4 次
这道题还是相当有难度的,因为这里涉及到了区间的合并、重叠问题,(不过本题可以取巧,利用链表存储区间,再在遍历链表的过程中进行区间合并即可),首先我们定义一个set,用来存储区间,set中存储的是某个区间的映射 :l * base+r,要得到该区间很简单,该元素除以base为左端点,模上base为右端点(需要满足base一直大于右端点,观察数据范围设置一个合适的值即可),这样做是无需我们重载小于号了,在set中会自动按该映射的大小来从小到大进行排序,这里的操作和我们常用的将二维数组坐标转化为一维类似。
对于得到区间没什么好说的,遍历set加入区间即可。加入区间才是本题的难点,首先我们得到该val的映射key=val*base+vak,然后去set中利用lower_bound找到映射大于等于他的位置。如果找不到直接在set中插入key并返回即可,而找到的情况就需要我们进行思考了。
这里我们已经得到了第一个大于等于要插入点的区间。我们考虑合并的情况:
(1):由于该映射比key要大,所以其左右端点l ,r 起码有一个是大于我们的val的。如果val+1>=l的话就说明该点可以加入到该区间中并且val是新的左端点,r是新的右端点(举个例子:val的区间为[2,2],找到的区间可能为 [2,2],[2,3],[3,5] ),这个时候先删除找到的区间然后更新左右区间插入到set中即可,这是区间为[val,r]。
(2):如果val+l>=l没有满足,就说明set中没有比val的区间大的,我们直接插入[val,val]即可。
这里书写代码的时候还是有些细节的,在第一种情况下我们先删除区间[l,r],第二种情况下我们让r=val,判断完两种情况后都让l=val,并插入[l,r](当然也可以不这么写,这样只是提高了一些复用性)。
比key大的区间我们考虑完毕了,现在考虑比key小的,这里就利用之前我们找到的迭代器让他自减即可(不为begin的情况下),其比key小也说明了两种情况,我们将其的左右端点称为x,y:
(1)x<=val&&y>=val:由于我们映射得到的key,左端点是站主导因素的所以这种情况是可能发生的。比如key的区间为[2,2],找到的区间为[1,3],这种情况下,我们需要删除之前插入的[l,r],因为新的区间就是[x,y]。
(2)当不满足上述条件的时候我们考虑y与l的关系,如果y+1>=l,二者就可以合并为新区间[x,r](比如[x,y]=[1,5],[l,r]=[4,8]或者[6,8]或者[5,8]),这个时候删除二者原区间在插入[x,r]即可。
那么这样我们就可以看成本题区间的合并和插入分为五种情况
(1):区间正好位于两个区间之间。(比如key=[3,5], 这两个区间为[1,1], [7,8])
(2):区间插入了某个区间内部。(比如key=[3,5],该区间为[2,6]或者[3,6]或者[2,5]或者[1,6])
(3):区间的右端点和某个区间的左端点可以合并。(比如key=[3,3],该区间为[3,8])
(4):区间的左端点和某个区间的右端点可以合并。(比如key=[4,5],该区间为[1,3]或者[2,4])
(5):区间的插入可以使某两个区间合并(例如key=[3,5],这两个区间为[2,4],[6,8]),只不过按照我们的思路是可以解决这种情况的所以没有单独列出来。
class SummaryRanges {
#define base 10001
set<int> s;
public:
SummaryRanges() {
s.clear();
}
void addNum(int val) {
int key=val*base+val;
if(s.size()==0){
s.insert(key);
return;
}
int l=val,r=val;
auto it=s.lower_bound(key);
if(it!=s.end()){
l=(*it)/base;
r=(*it)%base;
if(val+1>=l){
s.erase(l*base+r);
}else {
r=val;
}
l=val;
}
s.insert(l*base+r);
it=s.find(l*base+r);
if(it!=s.begin()){
it--;
int x=(*it)/base;
int y=(*it)%base;
if(x<=l&&y>=r){
s.erase(l*base+r);
}else if(y+1>=l){
s.erase(x*base+y);
s.erase(l*base+r);
s.insert(x*base+r);
}
}
}
vector<vector<int>> getIntervals() {
vector<vector<int>> ans;
for(auto i:s){
ans.push_back({i/base,i%base});
}
return ans;
}
};