2250. Count Number of Rectangles Containing Each Point
Problem Description
The problem presents us with a set of rectangles and a set of points. Each rectangle is defined by its length and height, with the bottom-left corner fixed at the origin (0, 0) and the top-right corner at coordinates (length, height)
. The points are given with their x and y coordinates.
Your task is to determine for each point how many rectangles out of the given set contain it. A point is considered contained within a rectangle if it lies inside the rectangle or on its edges, meaning the point's x-coordinate is less than or equal to the rectangle's length and its y-coordinate is less than or equal to the rectangle's height.
To sum it up: for every point given, count how many rectangles have both their length and height greater than or equal to the point's x and y coordinates, respectively.
Intuition
The intuition behind the solution involves preprocessing the rectangles to make the query for each point efficient. Since we only care about whether a point is contained within a rectangle, we can organize our rectangles by their heights. For each possible height, we keep a list of lengths of rectangles that have at least that height.
Once we have this structure, we can quickly determine how many rectangles contain a point with a given y-value. For a point with coordinates (x, y), we need to count the number of rectangles that have a height >= y and a width >= x.
This is where binary search comes into play. Because each list of lengths for a given height is sorted, we can use binary search to efficiently find the index of the smallest length that is still >= x. All lengths to the right of this index will also be >= x, so the number of such lengths is the total length of the list minus the index.
By summing up the counts for each height from y to the maximum possible height (which in this case is set to 100, the assumed maximum height), we get the total number of rectangles that contain the point. We repeat this process for each point and return the accumulated counts as our answer.
Learn more about Binary Search and Sorting patterns.
Solution Approach
The implementation of the solution can be divided into the following steps:
-
First, we initialize a dictionary (
defaultdict
) namedd
that will hold lists of rectangle lengths indexed by their heights. -
We then iterate through each rectangle in the
rectangles
list, add the length of each rectangle to the corresponding list ind
indexed by its height. -
After storing all the rectangles, we sort each list of lengths in
d
. This allows us to apply binary search on these lists. -
We then initialize an empty list
ans
which will hold our final counts for each point. -
Now we iterate through each given point in
points
. For the current point, we start a counter at 0. Then we check every possible rectangle height starting from the point's y-coordinate up to the maximum height (which is set to 101 to include the maximum height of 100). -
For each height
h
that is equal to or greater than the y-coordinate of the point, we get the list of rectangle lengthsxs
from the dictionaryd
. -
We perform a binary search using the
bisect_left
function from Python’sbisect
module to find the index where the point's x-coordinate could be inserted into thexs
list to maintain sorted order. This index also represents the number of lengths that are less than the point's x-coordinate. -
To find the number of lengths that are greater than or equal to the point's x-coordinate, we subtract the found index from the total number of lengths (
len(xs) - bisect_left(xs, x)
). -
We add this number to our counter
cnt
and, after checking all the possible heights, appendcnt
to ourans
list. -
Finally, after calculating the counts for all the points, we return the list
ans
as our output.
Let's go through an example to better understand the algorithm using its data structures and patterns:
- Suppose we have
rectangles = [[1,2], [2,3], [2,5]]
andpoints = [[1,1], [1,4]]
. - Our dictionary
d
after processing rectangles will look like:{2: [1], 3: [2], 5: [2]}
- After sorting, it will be
{2: [1], 3: [2], 5: [2]}
(no change in this case because there's only one length for each height). - Now for point
[1,1]
, we look atd[1]
upwards:d[1]
doesn't exist, butd[2]
does, and since1
can be inserted at index 0 in[1]
, there is1 - 0 = 1
rectangle that contains it; looking atd[3]
andd[5]
, both have lists[2]
which would include the point since thebisect_left([2], 1)
would give 0 resulting again in one rectangle each. Hencecnt = 1+1+1
. - The process is repeated for the second point
[1,4]
, where now onlyd[5]
can include the point, resulting incnt = 1
. - Finally, we have the answer
[3,1]
.
This solution utilizes a combination of hashmap to collate per-height rectangle lengths, sorting to prepare for binary search, and binary search to effectively answer queries for each point on how many rectangles contain it.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example to illustrate the approach provided in the solution. We have a set of rectangles and a set of points defined as follows:
rectangles = [[1,2], [2,3], [2,5]]
points = [[1,1], [1,4]]
Step 1: The dictionary d
is initialized. After processing rectangles
, d
is filled as follows:
- For the rectangle
[1,2]
, we add1
to the list atd[2]
. - For the rectangle
[2,3]
, we add2
to the list atd[3]
. - For the rectangle
[2,5]
, we add2
to the list atd[5]
.
The dictionary now looks like this:
d = { 2: [1], 3: [2], 5: [2] }
Step 2: As each list only contains one element, the lists are already sorted. If there were multiple elements, we would sort them to efficiently apply binary search later on.
Step 3: We initialize ans
as an empty list to hold the count for each point.
Step 4-9: We iterate through the points [1,1]
and [1,4]
to count how many rectangles contain these points:
-
For point
[1,1]
, we start with a countercnt
set to 0. We look at possible heights starting fromy=1
:- At height
h=2
(sinced[1]
doesn't exist), we find the list[1]
. Since1
can be inserted at index0
to maintain order, it means there is1 - 0 = 1
rectangle of height at least 2 that contains the point. - At height
h=3
,bisect_left([2], 1)
is0
, so there is1 - 0 = 1
rectangle of height at least 3 that contains the point. - At height
h=5
,bisect_left([2], 1)
is again0
, so there is another rectangle here. The accumulated countcnt
becomes1 + 1 + 1 = 3
. We append this count toans
.
- At height
-
For point
[1,4]
,cnt
is again set to 0. Since we only have heights up to5
, we look at:- At height
h=5
,bisect_left([2], 1)
gives0
, so there is1 - 0 = 1
rectangle of height at least 5 that contains the point. No other heights are considered since they are less than the y-coordinate of the point. The final count for this point is1
, which we append toans
.
- At height
Step 10: The final output [3,1]
is obtained after processing both points, which indicates that the first point [1,1]
is contained within 3
rectangles and the second point [1,4]
is contained within 1
rectangle.
In this example, we see the practical use of organizing rectangle dimensions by height to efficiently query with binary search the count of rectangles that include each point.
Solution Implementation
1from collections import defaultdict
2from bisect import bisect_left
3
4class Solution:
5 def countRectangles(self, rectangles: List[List[int]], points: List[List[int]]) -> List[int]:
6 # Create a dictionary to store the x-coordinates grouped by their y-coordinates.
7 y_to_xs = defaultdict(list)
8
9 # Go through each rectangle and group x-coordinates by y.
10 for x, y in rectangles:
11 y_to_xs[y].append(x)
12
13 # Sort each list of x-coordinates for binary search efficiency.
14 for y_coordinates in y_to_xs.values():
15 y_coordinates.sort()
16
17 # Initialize the list to hold the number of rectangles covering each point.
18 results = []
19
20 # For each point, count the number of rectangles that can cover it.
21 for point_x, point_y in points:
22 count = 0
23 # Check each possible y-coordinate (height) at or above the point's y.
24 for height in range(point_y, 101):
25 # Retrieve the list of x-coordinates at the current height.
26 x_coordinates_at_height = y_to_xs[height]
27 # Use binary search to find the starting position where rectangles at this height can cover the point.
28 count += len(x_coordinates_at_height) - bisect_left(x_coordinates_at_height, point_x)
29 # Append the count to results list for the current point.
30 results.append(count)
31
32 return results
33
1class Solution {
2 public int[] countRectangles(int[][] rectangles, int[][] points) {
3 // The maximum possible y-coordinate of the rectangles, assuming it is <= 100
4 int maxYCoordinate = 101;
5
6 // Create a list of array lists to store the x-coordinates indexed by y-coordinates
7 List<Integer>[] coordinatesByHeight = new List[maxYCoordinate];
8
9 // Initialize the list of array lists
10 Arrays.setAll(coordinatesByHeight, k -> new ArrayList<>());
11
12 // Fill the list with x-coordinates for corresponding y-coordinates
13 for (int[] rectangle : rectangles) {
14 coordinatesByHeight[rectangle[1]].add(rectangle[0]);
15 }
16
17 // Sort each list of x-coordinates for binary search
18 for (List<Integer> list : coordinatesByHeight) {
19 Collections.sort(list);
20 }
21
22 // The length of the array containing query points
23 int numPoints = points.length;
24
25 // An array to store the counts of rectangles that contain each point
26 int[] ans = new int[numPoints];
27
28 // Iterate over each point to calculate the count of rectangles containing the point
29 for (int i = 0; i < numPoints; ++i) {
30 int x = points[i][0], y = points[i][1];
31 int count = 0;
32
33 // Evaluate for rectangles having a height greater than or equal to the y-coordinate of the point
34 for (int h = y; h < maxYCoordinate; ++h) {
35
36 // Get the list of x-coordinates for the specific y-coordinate
37 List<Integer> xCoordinates = coordinatesByHeight[h];
38
39 // Perform binary search to find the index where x-coordinate of rectangles
40 // is just greater than or equal to the x-coordinate of the point
41 int left = 0, right = xCoordinates.size();
42 while (left < right) {
43 int mid = (left + right) >> 1;
44 if (xCoordinates.get(mid) >= x) {
45 right = mid;
46 } else {
47 left = mid + 1;
48 }
49 }
50
51 // Since the list is sorted, all x-coordinates from 'left' to the end will have
52 // x-coordinates greater than or equal to the point's x-coordinate
53 count += xCoordinates.size() - left;
54 }
55
56 // Store the count in the answer array
57 ans[i] = count;
58 }
59 return ans;
60 }
61}
62
1#include <vector>
2#include <algorithm>
3
4class Solution {
5public:
6 // Method to count the number of rectangles that can encompass each of the points
7 std::vector<int> countRectangles(std::vector<std::vector<int>>& rectangles, std::vector<std::vector<int>>& points) {
8 // 'maxHeight' is a constant representing the maximum height we are considering
9 const int maxHeight = 101;
10 // Array 'heightToWidths' will store vectors of widths at each height index
11 std::vector<std::vector<int>> heightToWidths(maxHeight);
12
13 // Store widths in the corresponding height index and sort them
14 for (auto& rect : rectangles) {
15 heightToWidths[rect[1]].push_back(rect[0]);
16 }
17 for (auto& widths : heightToWidths) {
18 std::sort(widths.begin(), widths.end());
19 }
20
21 // Vector 'answers' will store the result for each point
22 std::vector<int> answers;
23
24 // Iterate through each point and count the number of rectangles that contain the point
25 for (auto& point : points) {
26 int x = point[0], y = point[1];
27 int count = 0;
28
29 // For heights from the point's y-coordinate to maxHeight,
30 // count widths that are equal or larger than the point's x-coordinate
31 for (int h = y; h < maxHeight; ++h) {
32 auto& widthsAtHeight = heightToWidths[h];
33 // The number of valid rectangles is the total number of widths at this height
34 // minus the number of widths that are smaller than the point's x-coordinate
35 count += widthsAtHeight.end() - std::lower_bound(widthsAtHeight.begin(), widthsAtHeight.end(), x);
36 }
37
38 // Add the count to the result vector
39 answers.push_back(count);
40 }
41
42 // Return the answer vector containing counts for each point
43 return answers;
44 }
45};
46
1function countRectangles(rectangles: number[][], points: number[][]): number[] {
2 // Declare a constant for the maximum dimension (101 here is chosen based on problem constraints)
3 const MAX_DIMENSION = 101;
4 // Initialize an array to store x-coordinates grouped by y-coordinate
5 let xMapsByHeight = Array.from({ length: MAX_DIMENSION }, () => []);
6
7 // Populate the xMapsByHeight with the x-coordinates of the rectangles
8 for (let [x, y] of rectangles) {
9 xMapsByHeight[y].push(x);
10 }
11
12 // Sort each group of x-coordinates to enable binary search
13 for (let xCoordinates of xMapsByHeight) {
14 xCoordinates.sort((a, b) => a - b);
15 }
16
17 // Initialize an array to store the final counts for each point
18 let counts = [];
19
20 // Iterate through each point to count the number of rectangles that can contain the point
21 for (let [px, py] of points) {
22 let count = 0;
23 // Check at or above the point's y-coordinate (as rectangles extend upwards)
24 for (let height = py; height < MAX_DIMENSION; height++) {
25 const xCoordinates = xMapsByHeight[height];
26 let left = 0;
27 let right = xCoordinates.length;
28 // Perform binary search to find the number of rectangles with x greater than or equal to point's x
29 while (left < right) {
30 let mid = (left + right) >> 1; // Equivalent to Math.floor((left + right) / 2)
31 if (px > xCoordinates[mid]) {
32 left = mid + 1;
33 } else {
34 right = mid;
35 }
36 }
37 // Count the rectangles by subtracting the index of first rectangle not less than the point's x
38 count += xCoordinates.length - right;
39 }
40 // Add the count to the counts array
41 counts.push(count);
42 }
43
44 // Return the array of counts for each point
45 return counts;
46}
47
Time and Space Complexity
Time Complexity
The time complexity of the code consists of multiple parts:
-
Building the dictionary
d
: We iterate over each rectangle inrectangles
, resulting inO(n)
operations, wheren
is the number of rectangles. At most, we are storing 100 keys (because heighth
could be from 1 to 100 according to the problem constraints). -
Sorting the lists in the dictionary: For each key in the dictionary, which represents a unique height
h
, we sort the list of associatedx
coordinates. The sort operation has a time complexity ofO(k log k)
for each list of lengthk
. Since there could be up ton
x
values per height, and there are at most 100 heights, the worst-case complexity of this part isO(100 * n log n)
. -
Processing the points: We iterate over every point in
points
, resulting inO(m)
operations, wherem
is the number of points. For each point(x, y)
, we have an inner loop where we iterate potentially fromy
to100
, let's denote this range asR = 100 - y + 1
. Inside this loop, we perform a binary search usingbisect_left
which has a complexity ofO(log k)
for each heighth
. The total time complexity for processing points translates toO(m*R*log n)
.
Combining these complexities, the overall time complexity of the function is O(n + 100 * n log n + m*R*log n)
. Since R
and the number 100
are constants, and in the context of big O notation we drop constants and lower-order terms, the dominant term is O(n log n + m*log n)
. When n
(the number of rectangles) is large compared to m
(the number of points), the time complexity approximates to O(n log n)
; otherwise, it will be closer to O((n + m) log n)
.
Space Complexity
The space complexity is determined by:
-
The dictionary
d
storage: The dictionary itself will takeO(h)
space, withh
being the unique heights, which is at most 100. Each list in the dictionary can collectively hold up toO(n)
coordinates. Therefore, the space complexity for the dictionary isO(h + n)
. Sinceh
is a constant (at most 100), we can simply sayO(n)
. -
The output list
ans
: It stores the result for each point, so it takesO(m)
space.
Thus, the final space complexity of the function is O(n + m)
, for storing all the rectangles and output results. In the contexts where n
is much larger than m
, it simplifies to O(n)
.
Learn more about how to find time and space complexity quickly using problem constraints.
What's the relationship between a tree and a graph?
Recommended Readings
Binary Search Speedrun For each of the Speedrun questions you will be given a binary search related problem and a corresponding multiple choice question The multiple choice questions are related to the techniques and template s introduced in the binary search section It's recommended that you have gone through at
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
LeetCode 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 we
Want a Structured Path to Master System Design Too? Don’t Miss This!