Problem
Given an infinite integer number line, you would like to build some blocks and obstacles on it. Specifically, you have to implement code which supports two types of operations:
[1, x]— builds an obstacle at coordinatexalong the number line. It is guaranteed that coordinatexdoes not contain any obstacles when the operation is performed.[2, x, size]— checks whether it’s possible to build a block of sizesizewhich ends immediately beforexon the number line. For example, ifx = 6andsize = 2, this operation checks coordinates4and5. Produces"1"if it is possible (no obstacles at the specified coordinates), or"0"otherwise. Note that this operation does not actually build the block, it only checks.
Given an array of operations containing both types of operations described above, return a binary string representing the outputs for all [2, x, size] operations.
Example
operations = [[1, 2],
[1, 5],
[2, 5, 2],
[2, 6, 3],
[2, 2, 1],
[2, 3, 2]]
solution(operations) = "1010"
Walkthrough:
[1, 2]— obstacle at2.[1, 5]— obstacle at5.[2, 5, 2]— checks coords3..4→ free →"1".[2, 6, 3]— checks coords3..5→5is blocked →"0".[2, 2, 1]— checks coord1..1→ free →"1".[2, 3, 2]— checks coords1..2→2is blocked →"0".
Output: "1010".
Approach
A [2, x, size] query asks: is there any obstacle in the closed interval [x - size, x - 1]?
Naively scanning every coordinate is too slow when x and size can be large. Instead, store obstacles in a sorted structure and ask a single range question per query.
TreeSet<Integer> (a red-black tree) is perfect:
add(x)— insert obstacle inO(log n).ceiling(low)— smallest obstacle>= lowinO(log n).
For a query with range [low, high] where low = x - size, high = x - 1:
- Get
c = obstacles.ceiling(low). - If
c == nullorc > high, no obstacle lies in the range → append"1". - Otherwise an obstacle sits inside the range → append
"0".
Complexity
- Time:
O(N log N)whereNis the number of operations. - Space:
O(K)whereKis the number of obstacles built.
Solution
import java.util.TreeSet;
class Solution {
public String solution(int[][] operations) {
TreeSet<Integer> obstacles = new TreeSet<>();
StringBuilder out = new StringBuilder();
for (int[] op : operations) {
if (op[0] == 1) {
obstacles.add(op[1]);
} else {
int x = op[1];
int size = op[2];
int low = x - size;
int high = x - 1;
Integer nearest = obstacles.ceiling(low);
if (nearest == null || nearest > high) {
out.append('1');
} else {
out.append('0');
}
}
}
return out.toString();
}
}
Why ceiling and not a manual scan
ceiling(low) returns the smallest obstacle that is >= low. If even that smallest one is already past high, the whole window [low, high] is guaranteed empty. No need to look further. This collapses a range-check into one O(log n) lookup.
Edge Cases
size == 0— range is empty (low > high), always"1". Theceilingcheck still works: any non-null result will be> high.- Negative coordinates — the number line is infinite in both directions;
TreeSet<Integer>handles negatives naturally. - Duplicate obstacle inserts — problem guarantees they won’t happen, so no defensive check needed.