You are given a list of rooms, where each room is represented as [roomId, size]
. You are also given a list of queries, each as [preferred, minSize]
. For each query, you need to find the roomId of the room that:
minSize
roomId
as close as possible to preferred
(absolute difference minimized)-1
for that query
The naive approach is to, for each query, scan all rooms and filter those with sufficient size, then pick the roomId closest to the preferred value. However, this brute-force method is inefficient for large inputs.
To optimize, we notice:
minSize
, we can process rooms in decreasing size order, adding eligible rooms as we go, and answer each query efficientlyHere's how we can solve the problem efficiently:
minSize
(descending), keeping track of their original indices:
minSize
) first, so we only add rooms that are big enough as we go.minSize
to our data structure.-1
Design choices:
Input:
rooms = [[2,3],[1,5],[3,7]]
queries = [[3,5],[3,3],[5,2]]
Step-by-step:
[[3,7],[1,5],[2,3]]
minSize
(descending): [(0,[3,5]), (1,[3,3]), (2,[5,2])]
[3, 3, 3]
Brute-force:
This is much more efficient and suitable for large datasets.
The Closest Room problem can be solved efficiently by sorting both rooms and queries, and using a sorted data structure to maintain eligible roomIds as we process each query. This allows us to quickly insert new rooms and find the closest roomId to the preferred value using binary search, resulting in a scalable and elegant solution. The key insight is to process queries in order of decreasing minSize
, so we only need to add each room once and always have the correct set of eligible rooms for each query.
from bisect import bisect_left, insort
def closestRoom(rooms, queries):
rooms.sort(key=lambda x: -x[1])
indexed_queries = sorted([(i, q[0], q[1]) for i, q in enumerate(queries)], key=lambda x: -x[2])
res = [-1] * len(queries)
room_ids = []
i = 0
n = len(rooms)
for idx, preferred, minSize in indexed_queries:
while i < n and rooms[i][1] >= minSize:
insort(room_ids, rooms[i][0])
i += 1
if not room_ids:
res[idx] = -1
continue
pos = bisect_left(room_ids, preferred)
candidates = []
if pos < len(room_ids):
candidates.append(room_ids[pos])
if pos > 0:
candidates.append(room_ids[pos-1])
# Find the closest
best = min(candidates, key=lambda x: (abs(x - preferred), x))
res[idx] = best
return res
#include <vector>
#include <algorithm>
#include <set>
using namespace std;
vector<int> closestRoom(vector<vector<int>>& rooms, vector<vector<int>>& queries) {
sort(rooms.begin(), rooms.end(), [](const vector<int>& a, const vector<int>& b) {
return a[1] > b[1];
});
vector<tuple<int,int,int>> indexed_queries;
for (int i = 0; i < queries.size(); ++i)
indexed_queries.push_back({i, queries[i][0], queries[i][1]});
sort(indexed_queries.begin(), indexed_queries.end(), [](const tuple<int,int,int>& a, const tuple<int,int,int>& b){
return get<2>(a) > get<2>(b);
});
set<int> room_ids;
vector<int> res(queries.size(), -1);
int i = 0, n = rooms.size();
for (auto& q : indexed_queries) {
int idx = get<0>(q), preferred = get<1>(q), minSize = get<2>(q);
while (i < n && rooms[i][1] >= minSize)
room_ids.insert(rooms[i++][0]);
if (room_ids.empty()) continue;
auto it = room_ids.lower_bound(preferred);
int best = -1;
if (it != room_ids.end()) best = *it;
if (it != room_ids.begin()) {
int cand = *prev(it);
if (best == -1 || abs(cand - preferred) < abs(best - preferred) || (abs(cand - preferred) == abs(best - preferred) && cand < best))
best = cand;
}
res[idx] = best;
}
return res;
}
import java.util.*;
class Solution {
public int[] closestRoom(int[][] rooms, int[][] queries) {
Arrays.sort(rooms, (a, b) -> b[1] - a[1]);
int n = queries.length;
int[][] q = new int[n][3];
for (int i = 0; i < n; ++i) {
q[i][0] = queries[i][0];
q[i][1] = queries[i][1];
q[i][2] = i;
}
Arrays.sort(q, (a, b) -> b[1] - a[1]);
TreeSet<Integer> roomIds = new TreeSet<>();
int[] res = new int[n];
Arrays.fill(res, -1);
int i = 0, m = rooms.length;
for (int[] query : q) {
int preferred = query[0], minSize = query[1], idx = query[2];
while (i < m && rooms[i][1] >= minSize) {
roomIds.add(rooms[i][0]);
i++;
}
if (roomIds.isEmpty()) continue;
Integer higher = roomIds.ceiling(preferred);
Integer lower = roomIds.floor(preferred);
int best = -1;
if (higher != null) best = higher;
if (lower != null) {
if (best == -1 || Math.abs(lower - preferred) < Math.abs(best - preferred) || (Math.abs(lower - preferred) == Math.abs(best - preferred) && lower < best))
best = lower;
}
res[idx] = best;
}
return res;
}
}
function closestRoom(rooms, queries) {
rooms.sort((a, b) => b[1] - a[1]);
let indexedQueries = queries.map((q, i) => [i, q[0], q[1]]);
indexedQueries.sort((a, b) => b[2] - a[2]);
let res = Array(queries.length).fill(-1);
let roomIds = [];
let i = 0;
for (const [idx, preferred, minSize] of indexedQueries) {
while (i < rooms.length && rooms[i][1] >= minSize) {
// Insert in sorted order (binary search)
let left = 0, right = roomIds.length;
while (left < right) {
let mid = (left + right) >> 1;
if (roomIds[mid] < rooms[i][0]) left = mid + 1;
else right = mid;
}
roomIds.splice(left, 0, rooms[i][0]);
i++;
}
if (roomIds.length === 0) continue;
// Binary search for closest
let left = 0, right = roomIds.length - 1;
while (left < right) {
let mid = (left + right) >> 1;
if (roomIds[mid] < preferred) left = mid + 1;
else right = mid;
}
let candidates = [];
if (left < roomIds.length) candidates.push(roomIds[left]);
if (left > 0) candidates.push(roomIds[left - 1]);
let best = candidates[0];
for (let cand of candidates) {
if (Math.abs(cand - preferred) < Math.abs(best - preferred) ||
(Math.abs(cand - preferred) === Math.abs(best - preferred) && cand < best)) {
best = cand;
}
}
res[idx] = best;
}
return res;
}