Bird
Raised Fist0
Interview Prepcustom-data-structureshardGoogleAmazon

Range Module (Add/Remove/Query Ranges)

Choose your preparation mode4 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
</>
IDE
class RangeModule: def __init__(self): pass def addRange(self, left: int, right: int) -> None: pass def queryRange(self, left: int, right: int) -> bool: pass def removeRange(self, left: int, right: int) -> None: passpublic class RangeModule { public RangeModule() {} public void addRange(int left, int right) {} public boolean queryRange(int left, int right) { return false; } public void removeRange(int left, int right) {} }class RangeModule { public: RangeModule() {} void addRange(int left, int right) {} bool queryRange(int left, int right) { return false; } void removeRange(int left, int right) {} };class RangeModule { constructor() {} addRange(left, right) {} queryRange(left, right) { return false; } removeRange(left, right) {} }
class RangeModule:
    def __init__(self):
        # Write your solution here
        pass
    def addRange(self, left: int, right: int) -> None:
        pass
    def queryRange(self, left: int, right: int) -> bool:
        pass
    def removeRange(self, left: int, right: int) -> None:
        pass
class RangeModule {
    public RangeModule() {
        // Write your solution here
    }
    public void addRange(int left, int right) {
    }
    public boolean queryRange(int left, int right) {
        return false;
    }
    public void removeRange(int left, int right) {
    }
}
#include <vector>
using namespace std;

class RangeModule {
public:
    RangeModule() {
        // Write your solution here
    }
    void addRange(int left, int right) {
    }
    bool queryRange(int left, int right) {
        return false;
    }
    void removeRange(int left, int right) {
    }
};
class RangeModule {
    constructor() {
        // Write your solution here
    }
    addRange(left, right) {
    }
    queryRange(left, right) {
        return false;
    }
    removeRange(left, right) {
    }
}
Coming soon
0/10
Common Bugs to Avoid
Wrong: queryRange returns false for a range fully covered by merged intervalsIntervals are not merged correctly on addRange, leaving gaps between intervals.Merge intervals by expanding boundaries when overlapping or adjacent: dp[i][w] = max(dp[i-1][w], val[i-1] + dp[i-1][w-wt[i-1]])
Wrong: queryRange returns true for a range partially removedremoveRange does not split intervals properly, leaving removed parts still tracked.Split intervals at removal boundaries and remove only overlapping parts.
Wrong: queryRange returns true when no intervals existEmpty intervals list not handled; queryRange returns true by default or errors.Return false if intervals list is empty or no interval covers the query range.
Wrong: TLE on large inputsLinear scan of intervals for each operation causing O(n) per call.Use balanced tree or binary search to achieve O(log n) per operation.
Test Cases
t1_01basic
Input["addRange(10, 20)","removeRange(14, 16)","queryRange(10, 14)","queryRange(13, 15)","queryRange(16, 17)"]
Expected["null","null","true","false","true"]

After adding [10,20), removing [14,16) splits the range into [10,14) and [16,20). Querying [10,14) returns true, [13,15) returns false because 14-15 is removed, and [16,17) returns true.

t1_02basic
Input["addRange(5, 10)","addRange(15, 20)","removeRange(7, 17)","queryRange(5, 7)","queryRange(10, 15)","queryRange(17, 20)"]
Expected["null","null","null","true","false","true"]

After adding [5,10) and [15,20), removing [7,17) removes [7,10) and [15,17). Querying [5,7) returns true, [10,15) returns false (gap), and [17,20) returns true.

t2_01edge
Input["queryRange(0, 1)"]
Expected["false"]

No ranges added; querying any range should return false.

t2_02edge
Input["addRange(5, 6)","queryRange(5, 6)","removeRange(5, 6)","queryRange(5, 6)"]
Expected["null","true","null","false"]

Single element range added and removed; queries reflect correct presence and absence.

t2_03edge
Input["addRange(0, 10)","queryRange(0, 10)","removeRange(0, 10)","queryRange(0, 10)"]
Expected["null","true","null","false"]

Querying exact boundary intervals returns true before removal and false after removal.

t2_04edge
Input["addRange(10, 20)","addRange(12, 18)","queryRange(10, 20)"]
Expected["null","null","true"]

Adding a range completely inside an existing range should not change intervals; queryRange returns true.

t3_01corner
Input["addRange(1, 5)","addRange(2, 6)","queryRange(1, 6)"]
Expected["null","null","true"]

Overlapping addRange calls should merge intervals correctly so queryRange returns true.

t3_02corner
Input["addRange(1, 3)","addRange(5, 7)","removeRange(2, 6)","queryRange(1, 2)","queryRange(3, 5)","queryRange(6, 7)"]
Expected["null","null","null","true","false","true"]

Removing a range that partially overlaps multiple intervals splits them correctly.

t3_03corner
Input["addRange(1, 4)","addRange(4, 7)","queryRange(1, 7)"]
Expected["null","null","true"]

Adjacent intervals should merge so queryRange returns true for combined range.

t4_01performance
Input{"calls":[{"method":"addRange","args":[0,1000000000]},{"method":"removeRange","args":[1,999999999]},{"method":"queryRange","args":[0,1]},{"method":"queryRange","args":[999999999,1000000000]},{"method":"queryRange","args":[1,999999999]}]}
⏱ Performance - must finish in 2000ms

Large range add and remove operations with queries test O(log n) complexity; n=5 calls but with large intervals to test performance of interval merging and splitting.

Practice

(1/5)
1. You need to design a data structure that supports fast search, insertion, and deletion of integers, with average logarithmic time complexity. The structure should maintain multiple levels of linked lists with probabilistic promotion of nodes to higher levels. Which approach best fits this requirement?
easy
A. A multi-level linked list with randomized node promotion (Skiplist)
B. A hash table with chaining for collision resolution
C. A balanced binary search tree like AVL or Red-Black Tree
D. A simple sorted singly linked list with linear search

Solution

  1. Step 1: Identify the need for probabilistic multi-level structure

    The problem requires average O(log n) operations with linked list structure and random promotion of nodes to higher levels.
  2. Step 2: Match with known data structures

    Only a Skiplist uses multiple levels of linked lists with randomized promotion to achieve logarithmic average time complexity.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Skiplist matches all requirements including probabilistic multi-level design [OK]
Hint: Skiplist uses multi-level linked lists with random promotion [OK]
Common Mistakes:
  • Confusing balanced BSTs with linked list structures
  • Assuming hash tables provide order-based search
  • Thinking linear lists can achieve logarithmic search
2. Given the following code snippet for the optimal Twitter design, what is the output of getNewsFeed(1) after these operations?
twitter = Twitter()
twitter.postTweet(1, 5)
twitter.postTweet(2, 6)
twitter.followees[1] = {2}
print(twitter.getNewsFeed(1))
Assume getNewsFeed returns a list of tweetIds sorted from most recent to oldest.
easy
A. [6]
B. [5, 6]
C. [5]
D. [6, 5]

Solution

  1. Step 1: Trace postTweet calls

    User 1 posts tweet 5 at time=1, user 2 posts tweet 6 at time=2.
  2. Step 2: Trace getNewsFeed(1)

    User 1 follows user 2, so heap contains tweets with times 1 (tweet 5) and 2 (tweet 6). Max heap pops tweet 6 first, then tweet 5.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Heap orders tweets by descending time, so output is [6, 5] [OK]
Hint: Heap orders tweets by negative time for max behavior [OK]
Common Mistakes:
  • Returning tweets in posting order instead of time order
  • Ignoring followees' tweets
3. You need to design a cache that evicts the least frequently used item when full, and among items with the same frequency, evicts the least recently used one. Which approach guarantees O(1) average time complexity for both get and put operations?
easy
A. Using a simple hash map with frequency counts and sorting keys on each eviction
B. Maintaining a frequency map of doubly linked lists to track keys by frequency and recency
C. Using a min-heap keyed by frequency and timestamp to evict the least frequently used item
D. Using a single doubly linked list ordered by frequency and recency

Solution

  1. Step 1: Understand the eviction criteria

    The cache must evict the least frequently used key, and if multiple keys share the same frequency, evict the least recently used among them.
  2. Step 2: Identify data structures supporting O(1) operations

    A frequency map with doubly linked lists allows grouping keys by frequency and maintaining recency order within each frequency group, enabling O(1) updates and eviction.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Frequency map + doubly linked lists is the classic LFU O(1) approach [OK]
Hint: Frequency map + doubly linked lists enable O(1) LFU cache [OK]
Common Mistakes:
  • Using sorting on eviction is too slow
  • Min-heap adds O(log n) overhead
  • Single list can't maintain frequency and recency efficiently
4. Examine the following buggy remove method from the optimized Insert Delete GetRandom O(1) with duplicates data structure. Which line contains the subtle bug that can cause incorrect behavior or runtime errors?
def remove(self, val: int) -> bool:
    if val not in self.idx_map or not self.idx_map[val]:
        return false
    remove_idx = self.idx_map[val].pop()
    last_val = self.nums[-1]
    self.nums[remove_idx] = last_val
    self.idx_map[last_val].add(remove_idx)
    # BUG: missing discard of old index for last_val
    self.nums.pop()
    if not self.idx_map[val]:
        del self.idx_map[val]
    return true
medium
A. Missing line to discard old index of last_val from idx_map[last_val]
B. Line adding remove_idx to idx_map[last_val] (self.idx_map[last_val].add(remove_idx))
C. Line popping remove_idx from idx_map[val] (remove_idx = self.idx_map[val].pop())
D. Line removing the last element from nums (self.nums.pop())

Solution

  1. Step 1: Understand index updates during removal

    When swapping last_val into remove_idx, we must update idx_map[last_val] by adding remove_idx and discarding the old index (len(nums)-1).
  2. Step 2: Identify missing discard

    The code adds remove_idx to idx_map[last_val] but does not discard the old index, causing stale indices and incorrect removals later.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Missing discard leads to incorrect index tracking [OK]
Hint: Always discard old indices after swapping during removal [OK]
Common Mistakes:
  • Forgetting to discard old index after swap
  • Removing wrong element from idx_map
  • Incorrect pop from idx_map[val]
5. Suppose the stack is modified to allow reusing popped elements by pushing them back later, and the increment operation must still work correctly. Which modification to the optimal lazy increment approach is necessary to maintain correctness?
hard
A. Store increments per element rather than per index, requiring a more complex data structure.
B. Reset the entire increments array to zero on each push to avoid stale increments.
C. Track increments with a stack of (index, increment) pairs and apply them on pop accordingly.
D. No change needed; the existing lazy increment array works correctly with reused elements.

Solution

  1. Step 1: Understand reuse impact

    Reusing popped elements means the same stack positions may hold different elements over time, so increments per index become invalid.
  2. Step 2: Modify increment tracking

    To maintain correctness, increments must be tracked per element, not per index, requiring a data structure that associates increments with elements themselves.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Per-element increments prevent stale increment application on reused positions [OK]
Hint: Reusing elements breaks index-based increments; track per element [OK]
Common Mistakes:
  • Assuming no change needed
  • Resetting increments array loses valid increments