2250. Count Number of Rectangles Containing Each Point
Problem Description
You are given a set of rectangles and a set of points. Each rectangle has its bottom-left corner fixed at the origin (0, 0) and is defined by its length and height. Your task is to determine how many rectangles contain each given point.
Input Details:
rectangles[i] = [li, hi]: The i-th rectangle extends from (0, 0) to (li, hi), where li is the length (x-dimension) and hi is the height (y-dimension)points[j] = [xj, yj]: The j-th point is located at coordinates (xj, yj)
Containment Rules: A rectangle contains a point if:
- The point's x-coordinate is between 0 and the rectangle's length (inclusive):
0 <= xj <= li - The point's y-coordinate is between 0 and the rectangle's height (inclusive):
0 <= yj <= hi - Points on the edges or corners of rectangles are considered contained
Output: Return an array count where count[j] represents the number of rectangles that contain the j-th point.
Solution Approach: The solution groups rectangles by their height values in a dictionary. For each height, it maintains a sorted list of the corresponding lengths. When checking a point (x, y), it iterates through all heights greater than or equal to y (since rectangles with smaller heights cannot contain the point). For each valid height, it uses binary search to count how many rectangles at that height have a length greater than or equal to x.
The key optimization is using bisect_left to efficiently find the position where x would be inserted in the sorted list of lengths, then calculating how many rectangles have length >= x by subtracting this position from the total count.
Intuition
The naive approach would be to check every point against every rectangle, which would take O(n * m) time where n is the number of rectangles and m is the number of points. We need a smarter way to count containment.
The key insight is that for a point (x, y) to be inside a rectangle, the rectangle must satisfy two conditions:
- Its height must be at least y (height >= y)
- Its length must be at least x (length >= x)
Since all rectangles start at the origin (0, 0), we can think of this problem geometrically: we need rectangles whose top-right corner is "above and to the right" of our point.
This suggests organizing rectangles by one dimension to make searching easier. If we group rectangles by their height, then for any point (x, y), we only need to check rectangles with height >= y. This eliminates many unnecessary comparisons.
For each valid height group, we still need to count how many rectangles have length >= x. If we pre-sort the lengths within each height group, we can use binary search to quickly find this count. The position where x would be inserted tells us how many rectangles have length < x, so the remaining rectangles have length >= x.
The height constraint naturally filters out invalid rectangles (those too short), while binary search on the sorted lengths efficiently counts valid rectangles within each height group. Since rectangle heights are bounded (typically up to 100 in the problem constraints), iterating through valid heights is manageable, and the binary search makes counting within each group logarithmic rather than linear.
This approach transforms the problem from checking every rectangle individually to a more structured search that leverages sorting and binary search for efficiency.
Learn more about Binary Search and Sorting patterns.
Solution Implementation
1from collections import defaultdict 2from bisect import bisect_left 3from typing import List 4 5class Solution: 6 def countRectangles( 7 self, rectangles: List[List[int]], points: List[List[int]] 8 ) -> List[int]: 9 # Group rectangles by their height (y-coordinate) 10 # Key: height, Value: list of widths for rectangles with that height 11 height_to_widths = defaultdict(list) 12 13 # Store all rectangle widths grouped by their heights 14 for width, height in rectangles: 15 height_to_widths[height].append(width) 16 17 # Sort widths for each height to enable binary search 18 for height in height_to_widths.keys(): 19 height_to_widths[height].sort() 20 21 # Result list to store count of rectangles containing each point 22 result = [] 23 24 # For each query point, count rectangles that contain it 25 for point_x, point_y in points: 26 count = 0 27 28 # Check all rectangles with height >= point_y (heights from point_y to 100) 29 # A rectangle contains the point if its height >= point_y and width >= point_x 30 for rectangle_height in range(point_y, 101): 31 widths_at_height = height_to_widths[rectangle_height] 32 33 # Use binary search to find number of rectangles with width >= point_x 34 # bisect_left finds the insertion point for point_x in sorted list 35 # All elements from this index onwards have width >= point_x 36 count += len(widths_at_height) - bisect_left(widths_at_height, point_x) 37 38 result.append(count) 39 40 return result 411class Solution { 2 public int[] countRectangles(int[][] rectangles, int[][] points) { 3 int maxHeight = 101; 4 5 List<Integer>[] rectanglesByHeight = new List[maxHeight]; 6 Arrays.setAll(rectanglesByHeight, k -> new ArrayList<>()); 7 8 for (int[] rectangle : rectangles) { 9 rectanglesByHeight[rectangle[1]].add(rectangle[0]); 10 } 11 12 for (List<Integer> widthList : rectanglesByHeight) { 13 Collections.sort(widthList); 14 } 15 16 int numPoints = points.length; 17 int[] result = new int[numPoints]; 18 19 for (int i = 0; i < numPoints; i++) { 20 int pointX = points[i][0]; 21 int pointY = points[i][1]; 22 int rectangleCount = 0; 23 24 for (int height = pointY; height < maxHeight; height++) { 25 List<Integer> widthsAtHeight = rectanglesByHeight[height]; 26 27 // Binary search using first_true_index template 28 // Find first index where width >= pointX 29 int left = 0; 30 int right = widthsAtHeight.size() - 1; 31 int firstTrueIndex = widthsAtHeight.size(); // Default: no width >= pointX 32 33 while (left <= right) { 34 int mid = left + (right - left) / 2; 35 if (widthsAtHeight.get(mid) >= pointX) { 36 firstTrueIndex = mid; 37 right = mid - 1; 38 } else { 39 left = mid + 1; 40 } 41 } 42 43 // All rectangles from firstTrueIndex to end have width >= pointX 44 rectangleCount += widthsAtHeight.size() - firstTrueIndex; 45 } 46 47 result[i] = rectangleCount; 48 } 49 50 return result; 51 } 52} 531class Solution { 2public: 3 vector<int> countRectangles(vector<vector<int>>& rectangles, vector<vector<int>>& points) { 4 const int MAX_HEIGHT = 101; 5 vector<vector<int>> heightToWidths(MAX_HEIGHT); 6 7 for (auto& rectangle : rectangles) { 8 heightToWidths[rectangle[1]].push_back(rectangle[0]); 9 } 10 11 for (auto& widths : heightToWidths) { 12 sort(widths.begin(), widths.end()); 13 } 14 15 vector<int> result; 16 17 for (auto& point : points) { 18 int pointX = point[0]; 19 int pointY = point[1]; 20 int rectangleCount = 0; 21 22 for (int height = pointY; height < MAX_HEIGHT; ++height) { 23 auto& widthsAtHeight = heightToWidths[height]; 24 25 // Binary search using first_true_index template 26 int left = 0; 27 int right = widthsAtHeight.size() - 1; 28 int firstTrueIndex = widthsAtHeight.size(); // Default: no width >= pointX 29 30 while (left <= right) { 31 int mid = left + (right - left) / 2; 32 if (widthsAtHeight[mid] >= pointX) { 33 firstTrueIndex = mid; 34 right = mid - 1; 35 } else { 36 left = mid + 1; 37 } 38 } 39 40 rectangleCount += widthsAtHeight.size() - firstTrueIndex; 41 } 42 43 result.push_back(rectangleCount); 44 } 45 46 return result; 47 } 48}; 491function countRectangles(rectangles: number[][], points: number[][]): number[] { 2 const MAX_Y_VALUE = 101; 3 const rectanglesByY: number[][] = Array.from({ length: MAX_Y_VALUE }, () => []); 4 5 for (const [xCoord, yCoord] of rectangles) { 6 rectanglesByY[yCoord].push(xCoord); 7 } 8 9 for (const xCoordinates of rectanglesByY) { 10 xCoordinates.sort((a, b) => a - b); 11 } 12 13 const results: number[] = []; 14 15 for (const [pointX, pointY] of points) { 16 let rectangleCount = 0; 17 18 for (let currentY = pointY; currentY < MAX_Y_VALUE; currentY++) { 19 const xCoordinatesAtY = rectanglesByY[currentY]; 20 21 // Binary search using first_true_index template 22 // Find first index where x >= pointX 23 let left = 0; 24 let right = xCoordinatesAtY.length - 1; 25 let firstTrueIndex = xCoordinatesAtY.length; // Default: no x >= pointX 26 27 while (left <= right) { 28 const mid = Math.floor((left + right) / 2); 29 if (xCoordinatesAtY[mid] >= pointX) { 30 firstTrueIndex = mid; 31 right = mid - 1; 32 } else { 33 left = mid + 1; 34 } 35 } 36 37 rectangleCount += xCoordinatesAtY.length - firstTrueIndex; 38 } 39 40 results.push(rectangleCount); 41 } 42 43 return results; 44} 45Solution Approach
The solution groups rectangles by height and uses binary search to count containing rectangles.
Step 1: Group Rectangles by Height
height_to_widths = defaultdict(list) for width, height in rectangles: height_to_widths[height].append(width)
Step 2: Sort Width Lists
for height in height_to_widths.keys(): height_to_widths[height].sort()
Step 3: Query Using Binary Search Template
For each point (x, y), we iterate through valid heights (y to 100) and count rectangles with width >= x.
Binary Search Template (first_true_index):
We find the first index where width >= pointX:
feasible(idx) = widths[idx] >= pointX
Pattern: false, false, ..., true, true, true
left, right = 0, len(widths) - 1 first_true_index = len(widths) # Default: no width >= pointX while left <= right: mid = (left + right) // 2 if widths[mid] >= pointX: # feasible first_true_index = mid right = mid - 1 else: left = mid + 1 count = len(widths) - first_true_index
The count of rectangles with width >= pointX is len(widths) - first_true_index.
Time Complexity:
- Preprocessing: O(n log n) for sorting
- Per point query: O(h × log n) where h = 100 (max height)
- Total: O(n log n + m × h × log n)
Space Complexity: O(n) for storing grouped rectangles.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example to illustrate the solution approach.
Given:
- Rectangles:
[[4, 2], [3, 3], [5, 3], [2, 1]] - Points:
[[3, 2], [1, 3]]
Step 1: Organize Rectangles by Height
We group rectangles by their height values:
- Height 1: Rectangle
[2, 1]→ length = 2 - Height 2: Rectangle
[4, 2]→ length = 4 - Height 3: Rectangles
[3, 3]and[5, 3]→ lengths = 3, 5
Our dictionary d becomes:
d = { 1: [2], 2: [4], 3: [3, 5] }
Step 2: Sort Length Lists
After sorting (already sorted in this case):
d = { 1: [2], 2: [4], 3: [3, 5] # sorted: [3, 5] }
Step 3: Process Each Point
For Point 1: (3, 2)
- We need rectangles with height ≥ 2 and length ≥ 3
- Check height 2: lengths = [4]
bisect_left([4], 3) = 0(3 would be inserted at position 0)- Count:
1 - 0 = 1rectangle (the one with length 4)
- Check height 3: lengths = [3, 5]
bisect_left([3, 5], 3) = 0(3 already exists at position 0)- Count:
2 - 0 = 2rectangles (both length 3 and 5)
- Total for point (3, 2):
1 + 2 = 3rectangles
For Point 2: (1, 3)
- We need rectangles with height ≥ 3 and length ≥ 1
- Check height 3: lengths = [3, 5]
bisect_left([3, 5], 1) = 0(1 would be inserted at position 0)- Count:
2 - 0 = 2rectangles (both satisfy length ≥ 1)
- Total for point (1, 3):
2rectangles
Visual Representation:
Height 3 | +-----+ +-------+ | | | | | 2 | | Rect3| | Rect4 | | |[3,3]| | [5,3] | 1 | +-----+ +-------+ | +----+ | |Rect1| +---------+ | |[2,1]| | Rect2 | 0 +--+----+---+-[4,2]---+--------> Length 0 1 2 3 4 5 ^ ^ | | Point2 Point1 (1,3) (3,2)
Point (3, 2) is contained by rectangles [4, 2], [3, 3], and [5, 3]. Point (1, 3) is contained by rectangles [3, 3] and [5, 3].
Final Output: [3, 2]
Time and Space Complexity
Time Complexity: O(n log n + m * h * log n) where n is the number of rectangles, m is the number of points, and h is the range of possible heights (in this case, 101).
- Building the dictionary
dtakesO(n)time to iterate through all rectangles - Sorting all the x-coordinates for each unique height takes
O(n log n)in the worst case when all rectangles have different heights - For each of the
mpoints, we iterate through heights fromyto 100 (at most 101 iterations) - For each height, we perform a binary search using
bisect_leftwhich takesO(log k)wherekis the number of rectangles at that height - In the worst case, all rectangles could be at the same height, making each binary search
O(log n) - Total query time:
O(m * h * log n)
Space Complexity: O(n)
- The dictionary
dstores allnrectangle x-coordinates grouped by their y-coordinates, usingO(n)space - The answer list
ansusesO(m)space but this is typically considered as output space - The auxiliary space used by sorting is
O(1)since we sort in-place - Overall auxiliary space complexity is
O(n)
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Using Wrong Binary Search Template
The problem requires finding the first index where width >= pointX (first true pattern).
Wrong approach:
while left < right: mid = (left + right) >> 1 if widths[mid] >= pointX: right = mid else: left = mid + 1 return left # Returns left directly
Correct approach (first_true_index template):
first_true_index = len(widths) # Default while left <= right: mid = (left + right) // 2 if widths[mid] >= pointX: first_true_index = mid right = mid - 1 else: left = mid + 1 return first_true_index
2. Incorrect Default Value for first_true_index
When no width >= pointX exists, first_true_index should default to len(widths) so that count = len(widths) - first_true_index = 0.
Wrong: first_true_index = -1 (would give wrong count) Correct: first_true_index = len(widths)
3. Forgetting to Sort After Grouping
Binary search requires sorted data:
# Missing sort causes incorrect results! for height in height_to_widths.keys(): height_to_widths[height].sort() # Don't forget this!
4. Hardcoding Height Upper Bound
The code assumes heights ≤ 100 (LeetCode constraint). For robustness, calculate the actual max height:
max_height = max(h for _, h in rectangles) if rectangles else 0
What data structure does Breadth-first search typically uses to store intermediate states?
Recommended Readings
https assets algo monster cover_photos Binary_Search svg Binary Search Intuition Binary search is an efficient array search algorithm It works by narrowing down the search range by half each time If you have looked up a word in a physical dictionary you've already used binary search in real life Let's
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Don’t Miss This!