1. 题目来源
链接:1847. 最近的房间
2. 题目解析
很综合的一道题目,主要算法为 排序+二分+双指针。代码给出的注释已经很完善了。
思路:
- 将查询数组按照
size
,排序,排序后原下标被打乱,故需要记录原有的下标值。开一个结构体记录数据即可。 - 将房间按照
size
排序。 - 在查询数组中反向遍历,即按照
size
从大到小的顺序进行遍历,将所有大于size
加入有序集合set
中进行维护,从中选择最小的id
编号。 - 这样的遍历顺序就能保证,
room
越来越小,也就是采用了双指针的思想。 - 在
set
中运用lower_bound()
函数可以快速进行二分查找。 - 最后需要记得将下标还原到答案位置。
- 时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)
- 空间复杂度: O ( n ) O(n) O(n)
代码:
class Solution {
public:
struct Query {
int id, p, m; // 维护查询的原下标、preferredj、minSizej;
bool operator< (const Query &t) { // 按照 minSizej 升序排序
return m < t.m;
}
};
vector<int> closestRoom(vector<vector<int>>& rooms, vector<vector<int>>& queries) {
int n = queries.size();
vector<Query> q;
for (int i = 0; i < n; i ++ )
q.push_back({i, queries[i][0], queries[i][1]});
sort(q.begin(), q.end());
sort(rooms.begin(), rooms.end(), [](vector<int> &a, vector<int> &b) {
return a[1] < b[1]; // 按照 sizei 排序
});
const int INF = 1e9; // 技巧:保证 set 一定能查找到值,若查到无效值则说明是没找到
set<int> S{INF, -INF}; // set 维护满足 size 要求的所有 roomid,在其内进行二分查找,lower_bound() 操作
vector<int> res(n); // 直接开 n 大小的答案数组,排序后原查询数组混乱,通过 id 确定原下标
for (int i = n - 1, j = rooms.size() - 1; ~i; i -- ) { // 从后往前双指针,rooms size 只会越来越小
while (~j && rooms[j][1] >= q[i].m) S.insert(rooms[j -- ][0]); // 满足 size 的 roomid 全部入有序 set 集合
int p = q[i].p, id = q[i].id; // 得到每个查询的 preferredj,和原下标
auto k = S.lower_bound(p); // 找到满足的大于等于 preferredj 的 roomid
auto u = k;
u -- ; // 一定是严格小于 p 的那个 id
if (*k - p >= p - *u) res[id] = *u; // 相等的时候,取小的 id
else res[id] = *k; // 否则取大 id
if (abs(res[id]) == INF) res[id] = -1; // 找到边界了,为无效值,在 id 位置赋为 -1
}
return res;
}
};