1 有序集合
泛指set, map, pirority_queue等能够按照key进行排序,然后使用lower_bound和upper_bound来进行log(n)复杂度查询的基础数据结构
(注意priority_queue仅能够在堆顶进行操作)
示例如下:
set<int, std::less<int>> spareServers;
<被作者省略>
// request distribute, find the server
int tarServer = -1;
auto itr = spareServers.lower_bound(arrivalIdx % k);
// cout << "trying to find: " << arrivalIdx % k << endl;
if(itr != spareServers.end()) {
tarServer = *itr;
// cout << "find tar1: " << tarServer << endl;
} else { // search from the start just like search like a cycle
tarServer = *spareServers.lower_bound(0);
// cout << "find tar2: " << tarServer << endl;
}
2 具体示例
0363maxSumSubMatrix 最大子区域和
1 题目:
https://leetcode-cn.com/problems/max-sum-of-rectangle-no-larger-than-k/
2 解题思路:
- 1 普通思路:一维前缀和,按行计算前缀和,使用m x n的矩阵存储,然后计算矩形区域,则只需要O(m)的计算复杂度
- 2 优化:二维前缀和,pre[i][j]存储的是0,0到i,j处的矩形区域和,那么计算方式为:
preMat2[i+1][j+1] = preMat2[i][j+1] + preMat2[i+1][j] - preMat2[i][j] + matrix[i][j];
- 3 使用二位前缀和去计算子区域的和,搜索子区域的方式:
- 3.1 对于每个子区域的上下边界搜索左右边界,上下左边界搜索复杂度为o(n^2), 而后搜索左右边界,对于每一个右边界O(n),搜索左边界,什么样的左边界?left满足sum(up, down, 0, right) - k <= sum(up, down, 0, left), 也就是在[0, right]的范围内找到最小的大于sum(up, down, 0, right) - k的left,在遍历的过程中使用set存所有的sum(up, down, 0, right), 在这个set里面找left,使用lower_bound函数,只需要log(n)复杂度,于是总复杂度为 o(m^2 * nlog(n))
- 3.2 上述核心思想: 本来需要找right和left的对子,但是现在借助k,找left就变成在right的历史中搜索即可
- 3.2.1 原来思想:sum(up, down, 0, right) - sum(up, down, 0, left) <= k
- 3.2.2 新的思想:sum(up, down, 0, right) - k <= sum(up, down, 0, left)
class Solution {
public:
static constexpr int matLen = 102;
vector<vector<int>> preMat; // 2d prefix sum
int m = -1;
int n = -1;
int maxSumSubmatrix(vector<vector<int>>& matrix, int k) {
// cal 2d prefix sum
m = matrix.size();
n = matrix[0].size();
preMat.resize(m+1, vector<int>(n+1, 0));
for(int i = 0; i < m; ++i){
for(int j = 0; j < n; ++j){
preMat[i+1][j+1] = preMat[i][j+1] + preMat[i+1][j] - preMat[i][j] + matrix[i][j];
// cout << preMat[i+1][j+1] << endl;
}
}
// for each up and down, find max l,r
int res = INT_MIN;
for(int d = 0; d < m; ++d) {
for(int u = d; u >= 0; --u) {
set<int> lSet = {0};
for(int r = 0; r < n; ++r) {
int sumRight = sumRegion(u, d, 0, r);
set<int>::iterator lbPtr = lSet.lower_bound(sumRight - k);
lSet.insert(sumRight);
if(lbPtr != lSet.end()) {
res = max(res, sumRight - *lbPtr);
}
}
}
}
return res;
}
int sumRegion(int up, int down, int left, int right) {
return preMat[down+1][right+1] - preMat[down+1][left] - preMat[up][right+1] - preMat[up][left];
}
};
0699fallingSquares 掉落的方块
1 题目:
https://leetcode-cn.com/problems/falling-squares/
2 解题思路:
- 1 普通思路:使用一维数组模拟每个位置方块高度,复杂度为o(N^2),由于N很大,所以会超时
- 2 优化:使用坐标压缩,将所有方块的边界存储在2000以内(因为一共就1000个方块),然后更新高度就按照2000以内去更新即可
class Solution {
public:
vector<int> fallingSquares(vector<vector<int>>& positions) {
// coordinate compression
set<int, std::less<int>> coords;
for(vector<int>& coord : positions) {
coords.insert(coord[0]);
coords.insert(coord[0] + coord[1] - 1);
}
unordered_map<int, int> borderToIdx;
int t = 0;
for(auto& i : coords) {
borderToIdx[i] = t++;
}
// cal heights
vector<int> heights(t);
vector<int> res;
int curHeightest = INT_MIN;
for(vector<int>& square : positions) {
int l = borderToIdx[square[0]];
int r = borderToIdx[square[0] + square[1] - 1];
int h = square[1];
int updatedHeight = update(l, r, h, heights);
curHeightest = max(curHeightest, updatedHeight);
res.emplace_back(curHeightest);
}
return res;
}
// update and return updated height of [l, r]
int update(int l, int r, int h, vector<int>& heights) {
int oldHeight = INT_MIN;
for(int i = l; i <= r; ++i) {
oldHeight = max(oldHeight, heights[i]);
}
int newHeight = INT_MIN;
for(int i = l; i <= r; ++i) {
heights[i] = oldHeight + h;
}
return oldHeight + h;
}
};
0715 Range 模块 rangeModule
1 题目:
https://leetcode-cn.com/problems/range-module/
2 解题思路:
- 1 普通思路:使用map记录这些离散的区间
- 1.1 添加、删除: 遍历map,看map的每一个区间和当前区间的关系,这样处理最为合适
- 1.2 查询:使用upper_bound查询到第一个(左侧)比目标的left小的区间,然后看目标的left和right是否在map对应的区间内部
class RangeModule {
public:
map<int, int, std::less<int>> intervals;
RangeModule() {
}
void addRange(int left, int right) {
if(intervals.size() == 0) {
intervals[left] = right;
print();
return ;
}
for(auto it = intervals.begin(); it != intervals.end(); ++it) {
if(it->first >= left) {
if(it->first > right) {
continue;
}
while(it != intervals.end() && it->second <= right) {
it = intervals.erase(it);
}
if(it == intervals.end()){
intervals[left] = right;
} else {
if(it->first <= right) {
int newRight = it->second;
intervals.erase(it);
intervals[left] = newRight;
}else {
intervals[left] = right;
}
}
print();
return;
} else { // it->first < left
if(it->second < left) {
continue;
}
int newLeft = it->first;
while(it != intervals.end() && it->second <= right) {
it = intervals.erase(it);
}
if(it == intervals.end()){
intervals[newLeft] = right;
} else {
if(it->first <= right) {
int newRight = it->second;
intervals.erase(it);
intervals[newLeft] = newRight;
}else {
intervals[newLeft] = right;
}
}
print();
return;
}
}
intervals[left] = right;
print();
}
bool queryRange(int left, int right) {
if(intervals.empty()) {
return false;
}
auto lBig = intervals.upper_bound(left);
if(lBig != intervals.begin()) {
--lBig;
return lBig->second >= right;
} else {
return false;
}
return false;
}
void removeRange(int left, int right) {
// for(auto it = intervals.begin(); it != intervals.end(); ++it) {
// for(int i = 0)
// }
auto lBig = intervals.lower_bound(left);
int newLeft = 0;
if(lBig != intervals.begin()) {
// cout << "d1.5" << endl;
--lBig;
if(right < lBig->second) {
intervals[right] = lBig->second;
intervals[lBig->first] = left;
print();
return ;
} else {
// cout << "d2" << endl;
if(lBig->second > left) {
// cout << "d2.5" << endl;
intervals[lBig->first] = left;
}
}
++lBig;
while (lBig != intervals.end() && lBig->second < right) {
lBig = intervals.erase(lBig);
}
if(lBig != intervals.end()) {
if(lBig->first < right) {
int secondRight = lBig->second;
intervals.erase(lBig);
intervals[right] = secondRight;
}
}
} else {
while (lBig != intervals.end() && lBig->second < right) {
lBig = intervals.erase(lBig);
}
if(lBig != intervals.end()) {
if(lBig->first < right) {
int secondRight = lBig->second;
intervals.erase(lBig);
intervals[right] = secondRight;
}
}
}
print();
}
void print() {
// cout << "-----st-----" << endl;
// for(auto it = intervals.begin(); it != intervals.end(); ++it) {
// cout << it->first << " " << it->second << endl;
// }
// cout << "-----ed-----" << endl;
}
};
/**
* Your RangeModule object will be instantiated and called as such:
* RangeModule* obj = new RangeModule();
* obj->addRange(left,right);
* bool param_2 = obj->queryRange(left,right);
* obj->removeRange(left,right);
*/
0850. 矩形面积 II
1 题目:
https://leetcode-cn.com/problems/rectangle-area-ii/solution
2 解题思路:
- 1 参考官方思路的扫描线:https://leetcode-cn.com/problems/rectangle-area-ii/solution/ju-xing-mian-ji-ii-by-leetcode/
- 2 总结过程:
- 2.1 将一个矩形看成x1,x2,y1,ST; x1,x2,y2,ED;的两条线
- 2.2 而后用active记录还没有遇到ED的那些矩形的第一个扫描线集合,每回新来了一根线,则将active中的所有线和当前线计算对应面面积加起来
- 2.3 当active中遇到了矩形的ED,则将active中对应的矩形的ST删除
class Solution {
public:
static constexpr long long bigPrime = 1000000007;
int rectangleArea(vector<vector<int>>& rectangles) {
// lines
int ST = 0;
int ED = 1;
vector<vector<int>> lines;
for(auto& vec : rectangles) {
lines.emplace_back(vector<int>{vec[0], vec[2], vec[1], ST});
lines.emplace_back(vector<int>{vec[0], vec[2], vec[3], ED});
}
// sort the lines by the y
sort(lines.begin(), lines.end(),
[](vector<int>& a, vector<int>& b) {
return a[2] < b[2];
});
// scan the lines
vector<vector<int>> actives;
long long res = 0;
int lastY = lines[0][2];
for(auto& line : lines) {
int curY = line[2], type = line[3], x1 = line[0], x2 = line[1];
int width = 0;
int actX = -1;
// actives: those opend lines, sorted by y
for(auto& act : actives) {
// cout << "act: " << act[0] << " " << act[1] << endl;
actX = max(actX, act[0]);
width += max(act[1] - actX, 0);
actX = max(actX, act[1]);
}
res += static_cast<long long>(width) * (curY - lastY) % bigPrime;
// cout << "w: " << width << endl;
// add new actives
if(type == ST) {
actives.emplace_back(vector<int>{x1, x2, curY, type});
// cout << "insert x1,2: " << x1 << " " << x2 << endl;
// sort the opend lines of the started points
sort(actives.begin(), actives.end(),
[](vector<int>& line1, vector<int>& line2) {
return line1[0] < line2[0];
});
} else {
// find the active and rm it
for(int i = 0; i < actives.size(); ++i) {
if(actives[i][0] == x1 && actives[i][1] == x2) {
actives.erase(actives.begin() + i);
// cout << "earse x1,2: " << x1 << " " << x2 << endl;
break;
}
}
}
lastY = curY;
}
return res % bigPrime;
}
vector<vector<int>> getSkyLine(vector<vector<int>>& buildings) {
function<bool(pair<int, int>&, pair<int, int>&)> cmp =
[](pair<int, int>& a, pair<int, int>& b) {
return a.second < b.second;
};
// <rightBound, height>
priority_queue<pair<int, int>, vector<pair<int, int>>, function<bool(pair<int, int>&, pair<int, int>&)> > queue(cmp);
vector<int> bounds;
vector<vector<int>> res;
int n = buildings.size();
for(int i = 0; i < n; ++i) {
bounds.emplace_back(buildings[i][0]);
bounds.emplace_back(buildings[i][1]);
}
sort(bounds.begin(), bounds.end(), std::less<int>());
// std::cout << "c1" << endl;
int j = 0;
for(auto& curBound : bounds) {
// std::cout << "c " << j << endl;
// push buildings lefter than curBound until one righter meet
while(j < buildings.size() && curBound >= buildings[j][0]) {
queue.push(make_pair(buildings[j][1], buildings[j][2]));
++j;
}
// std::cout << "c " << "adf" << endl;
// pop out those rec unrelevant
while(!queue.empty() && curBound >= queue.top().first) {
queue.pop();
}
int curBoundHeight = queue.empty() ? 0 : queue.top().second;
// if(res.size() == 0 || res.back()[1] != curBoundHeight) {
// res.emplace_back(vector<int>{curBound, curBoundHeight});
// }
if(res.size() == 0 || res.back()[1] != curBoundHeight) {
res.emplace_back(vector<int>{curBound, curBoundHeight});
}
}
return res;
}
void print(vector<vector<int>>& vec) {
for(auto& v : vec) {
for(auto& i : v) {
cout << i << " ";
}
cout << endl;
}
}
};
0895maxFreqStack 最大频率栈
1 题目:
https://leetcode-cn.com/problems/maximum-frequency-stack/
2 解题思路:map/hash栈
- 1 参考官方思路的扫描线:https://leetcode-cn.com/problems/maximum-frequency-stack/solution/zui-da-pin-lu-zhan-by-leetcode/
- 2 总结过程:
- 2.1 首先用map维持每一个变量的频率
- 2.1.1 维持方法: push中对应的key加一,pop对应的key减一
- 2.2 如何获得当前最大频率?
- 2.2.1 在每个变量push和pop的时候我们可以获得对应的key的频率,那么所有的key的频率变动都是在push和pop执行完成之后,那么只需要用一个变量maxFreq维持最大频率即可
- 2.3 知道最大频率,如何获得当前最大频率的变量?
- 2.3.1 使用map<频率,频率对应的数的集合>来记录即可,然后用2.2中使用maxFreq获取最大频率对应的数字的集合,那如何从这个集合中获取在栈顶的数据呢?map<频率,频率对应的数的集合>中 频率对应的数的集合 使用stack去存就好了,因为stack的栈顶总是存着最新来的数据
- 2.4 一个实例: 3,3,3都是push,那么在频率为1,2,3的栈中,很自然的都有一个3,自然体现在哪里呢?现在pop一下,频率为3对应的stack为空,然后最大频率变为2,然后2里面同样是3
- 2.1 首先用map维持每一个变量的频率
class FreqStack {
public:
int opNum;
map<int, int> opNumMap;
map<int, int> freqMap;
map<int, vector<int>*> groupByFreq;
int maxFreq;
FreqStack() {
}
void push(int val) {
// cout << "pushing st: with max freq: " << maxFreq << endl;
freqMap[val]++;
if(groupByFreq[freqMap[val]] == nullptr) {
groupByFreq[freqMap[val]] = new vector<int>();
}
groupByFreq[freqMap[val]]->emplace_back(val);
maxFreq = max(maxFreq, freqMap[val]);
// cout << "pushing done: " << val << "with maxFreq: " << maxFreq << endl;
}
int pop() {
// cout << "pop st with maxFreq: " << maxFreq << endl;
// find biggest frequency and most newly element to rm
int popRes = groupByFreq[maxFreq]->back();
groupByFreq[maxFreq]->pop_back();
if(groupByFreq[maxFreq]->size() == 0) {
delete groupByFreq[maxFreq];
groupByFreq[maxFreq] = nullptr;
--maxFreq;
}
// cout << "pop ed" << popRes << " with maxFreq: " << maxFreq << endl;
freqMap[popRes]--;
return popRes;
}
};
/**
* Your FreqStack object will be instantiated and called as such:
* FreqStack* obj = new FreqStack();
* obj->push(val);
* int param_2 = obj->pop();
*/
1606busiestServers 找到处理最多请求的服务器
1 题目:
https://leetcode-cn.com/problems/find-servers-that-handled-most-number-of-requests/
2 解题思路:
- 1 自然的思路:对于每一个到来的请求,我们将服务器分为两部分,一部分是空闲服务器,一部分是繁忙服务器
- 1.1 如何快速找到空闲服务器?我们用set记录空闲服务器即可,两次使用lower_bound完成cycle查询
- 1.2 如何记录繁忙服务器?在每一个请求到来以及处理完成,都可能出现繁忙服务器的改动和空闲服务器的改动,于是采用map<到期时间,相应服务器列表>来存储繁忙服务器
- 1.3 如何处理请求处理完成时的服务器从繁忙变为空闲?
- 1.3.1 采用小根堆存储当前所有繁忙服务器的到期时间,那么只需要在请求到来的时候,将所有比当前请求小的到期时间的繁忙服务器变为空闲即可
class Solution {
public:
vector<int> busiestServers(int k, vector<int>& arrival, vector<int>& load) {
// using small root heap to store the smallest avaliable server
// auto cmp = [](const int& a, const int b)
set<int, std::less<int>> spareServers;
map<int, vector<int>> busyServers;
priority_queue<int, vector<int>, std::greater<int>> dues;
vector<pair<int, int>> serverSumLoad;
for(int i = 0; i < k; ++i) {
spareServers.insert(i);
serverSumLoad.push_back(make_pair(i,0));
}
// deal request
int reqNum = arrival.size();
int arrivalIdx = 0;
for(int arrival : arrival) {
// cout << "req: " << arrival << "load: " << load[arrivalIdx] << endl;
// release servers
while(!dues.empty() && dues.top() <= arrival) {
int due = dues.top();
dues.pop();
vector<int>& toRelease = busyServers[due];
for(auto i : toRelease) {
spareServers.insert(i);
// cout << "releasing " << i << endl;
}
// busyServers.erase(due);
}
// abandon the request
if(spareServers.empty()) {
++ arrivalIdx;
// cout << "abandon!" << endl;
continue;
}
// request distribute, find the server
int tarServer = -1;
auto itr = spareServers.lower_bound(arrivalIdx % k);
// cout << "trying to find: " << arrivalIdx % k << endl;
if(itr != spareServers.end()) {
tarServer = *itr;
// cout << "find tar1: " << tarServer << endl;
} else { // search from the start just like search like a cycle
tarServer = *spareServers.lower_bound(0);
// cout << "find tar2: " << tarServer << endl;
}
spareServers.erase(tarServer);
// set the server to busy
int due = arrival + load[arrivalIdx];
if(busyServers.find(due) == busyServers.end()) {
vector<int> tmpVec = {tarServer};
busyServers[due] = tmpVec;
dues.push(due);
// cout << "init busy due : " << tarServer << " to " << due << endl;
} else {
busyServers[due].push_back(tarServer);
// cout << "add busy due : " << tarServer << " to " << due << endl;
}
serverSumLoad[tarServer].second ++;
++ arrivalIdx;
}
sort(serverSumLoad.begin(), serverSumLoad.end(),[](
const pair<int, int>& p1, const pair<int, int>& p2
) {
return p1.second > p2.second;
});
// for(int ed = 0; ed < serverSumLoad.size(); ++ed) {
// cout << "server: " << serverSumLoad[ed].first << " > " << serverSumLoad[ed].second << endl;
// }
int ed = 1;
vector<int> res = {serverSumLoad[0].first};
for(; ed < serverSumLoad.size(); ++ed) {
if(serverSumLoad[ed].second != serverSumLoad[0].second) {
break;
}
res.emplace_back(serverSumLoad[ed].first);
}
return res;
}
};