Implement a Range Module with Add, Query, and Remove
Walmart LabsSoftware Engineer · Technical ScreenLast updated
Design a data structure that tracks coverage of real numbers using half-open intervals, supporting dynamic additions, removals, and continuous coverage queries. This tests ordered data structure manipulation, interval merging/splitting invariants, and binary search.
Problem Statement
A Range Module tracks ranges of numbers over the real number line using half-open intervals [left, right), which represents all numbers x such that left None: Adds the half-open interval [left, right) to the tracked set. Any overlapping or contiguous intervals must be merged.
query_range(left: int, right: int) -> bool: ReturnsTrueif every real number in[left, right)is currently tracked, andFalseotherwise.remove_range(left: int, right: int) -> None: Stops tracking every real number in[left, right). This may split existing intervals into multiple disjoint intervals.
Interface
class RangeTracker:
def __init__(self):
pass
def add_range(self, left: int, right: int) -> None:
pass
def query_range(self, left: int, right: int) -> bool:
pass
def remove_range(self, left: int, right: int) -> None:
pass
Examples
Example 1
tracker = RangeTracker()
tracker.add_range(10, 20)
tracker.remove_range(14, 16)
tracker.query_range(10, 14) # Returns True: [10, 14) is fully tracked
tracker.query_range(13, 15) # Returns False: 14 to 15 is not tracked
tracker.query_range(16, 17) # Returns True: [16, 17) is fully inside [16, 20)
tracker.add_range(14, 16)
tracker.query_range(10, 20) # Returns True: the hole was filled, merging back to [10, 20)
Example 2
tracker = RangeTracker()
tracker.add_range(1, 5)
tracker.add_range(5, 10)
# Contiguous intervals [1, 5) and [5, 10) merge into [1, 10)
tracker.query_range(1, 10) # Returns True
tracker.remove_range(3, 8)
# [1, 10) is split into [1, 3) and [8, 10)
tracker.query_range(1, 3) # Returns True
tracker.query_range(3, 4) # Returns False
tracker.query_range(8, 10) # Returns True
Constraints
1 <= left < right <= 10^9for all calls.- At most
10^4total calls will be made toadd_range,query_range, andremove_range.
Edge Cases
- Adding an interval that is completely contained within an existing interval.
- Removing a range that spans across multiple disjoint intervals, partially cutting off edges on both sides.
- Adjacent intervals sharing an endpoint (e.g.,
[1, 5)and[5, 10)) must merge inadd_range, but do not require merging across a removed boundary.
Hints
Hint 1
Maintain disjoint, sorted intervals at all times so that binary search can locate overlapping candidates in O(log n) time.
Hint 2
For query_range(left, right), binary search for the unique interval that could contain left; the query returns True if and only if that interval exists and extends to at least right.
Hint 3
For add_range and remove_range, locate the start and end insertion points, collect all affected intervals, and replace that slice with at most one or two updated intervals.
Follow-up questions
- How would you optimize this design if read queries (query_range) vastly outnumber write operations (add/remove)?
- How could this data structure be made thread-safe for concurrent read and write operations in a high-throughput microservice?
- If the coordinate values were floating-point numbers or arbitrarily large timestamps, what changes (if any) would you make to your interval representation?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.