[Hard] 850. Rectangle Area II
You are given a 2D array of axis-aligned rectangles. For each rectangle[i] = [xi1, yi1, xi2, yi2], where (xi1, yi1) is the bottom-left corner and (xi2, yi2) is the top-right corner of the ith rectangle.
Calculate the total area covered by all rectangles in the plane. Any area covered by two or more rectangles should only be counted once.
Return the total area. Since the answer may be too large, return it modulo 10^9 + 7.
Examples
Example 1:
Input: rectangles = [[0,0,2,2],[1,0,2,3],[1,0,3,1]]
Output: 6
Explanation: A total area of 6 is covered by all three rectangles, as illustrated in the picture.
From (1,1) to (2,2), the green and red rectangles overlap.
Example 2:
Input: rectangles = [[0,0,1000000000,1000000000]]
Output: 49
Explanation: The answer is 10^18 modulo (10^9 + 7), which is 49.
Constraints
1 <= rectangles.length <= 200rectangles[i].length == 40 <= xi1, yi1, xi2, yi2 <= 10^9xi1 <= xi2yi1 <= yi2
Thinking Process
- Sweep line: Convert 2D problem to 1D by sweeping along one axis
- Identify the pattern from constraints (sorted? graph? optimal substructure?).
- Write brute force first mentally, then optimize the bottleneck.
- Verify edge cases: empty input, single element, duplicates.
Common Approaches
Typical techniques for this pattern:
| Approach | Time | Space | Notes |
|---|---|---|---|
| Brute force (this problem) | Often O(n^2) or O(2^n) | O(n) | Baseline; clarifies the optimization target |
| Sort + scan | O(n log n) | O(1) | Pairs, intervals, greedy ordering |
| Hash map / set | O(n) | O(n) | Frequency, membership, two-sum style |
| Single-pass linear | O(n) | O(1) | Two pointers, sliding window, Kadane |
Solution
Time Complexity: O(n² log n) - Sorting events + O(n) per event for coverage calculation
Space Complexity: O(n) - For events and coordinate arrays
This solution uses a sweep line algorithm with coordinate compression to handle large coordinate values efficiently.
class Solution {
public:
int rectangleArea(vector<vector<int>>& rectangles) {
const int OPEN = 1, CLOSE = -1, MOD = 1E9 + 7;
vector<int> xCoords;
vector<array<int, 4>> events; // {y, type, x1, x2}
for(auto& rec : rectangles) {
xCoords.push_back(rec[0]);
xCoords.push_back(rec[2]);
events.push_back({rec[1], OPEN, rec[0], rec[2]});
events.push_back({rec[3], CLOSE, rec[0], rec[2]});
}
// Coordinate compression
sort(xCoords.begin(), xCoords.end());
xCoords.erase(unique(xCoords.begin(), xCoords.end()), xCoords.end());
unordered_map<int, int> xIdx;
for(int i = 0; i < xCoords.size(); i++) {
xIdx[xCoords[i]] = i;
}
// Sort events by y-coordinate (process OPEN before CLOSE at same y)
sort(events.begin(), events.end(), [](auto& a, auto& b){
if(a[0] != b[0]) return a[0] < b[0];
return a[1] > b[1];
});
int n = xCoords.size();
vector<int> count(n, 0); // Coverage count for each x-segment
long long rtn = 0;
int curY = events[0][0];
for(auto& e: events) {
int y = e[0], type = e[1], x1 = e[2], x2 = e[3];
// Calculate total covered length
long long coveredLen = 0;
for(int i = 0; i < n - 1; i++) {
if(count[i] > 0) {
coveredLen += xCoords[i + 1] - xCoords[i];
}
}
rtn = (rtn + coveredLen * (y - curY)) % MOD;
// Update coverage counts
int idx1 = xIdx[x1], idx2 = xIdx[x2];
for(int i = idx1; i < idx2; i++) {
count[i] += type;
}
curY = y;
}
return (int)rtn;
}
};
Solution Explanation
Approach: Brute force (this problem)
Key idea: 1. Sweep line: Convert 2D problem to 1D by sweeping along one axis
How the code works:
- Sweep line: Convert 2D problem to 1D by sweeping along one axis
- Identify the pattern from constraints (sorted? graph? optimal substructure?).
- Write brute force first mentally, then optimize the bottleneck.
- Verify edge cases: empty input, single element, duplicates.
Walkthrough — input rectangles = [[0,0,2,2],[1,0,2,3],[1,0,3,1]], expected output 6:
A total area of 6 is covered by all three rectangles, as illustrated in the picture. From (1,1) to (2,2), the green and red rectangles overlap.
| Solution | Time | Space | Notes |
|---|---|---|---|
| Sweep Line + Array | O(n² log n) | O(n) | Simple, good for n ≤ 200 |
| Sweep Line + Segment Tree | O(n log n) | O(n) | Optimal for large n |
How Solution 1 Works
- Event Generation:
- For each rectangle, create two events:
- OPEN event at bottom edge (y1): Start covering x-range [x1, x2]
- CLOSE event at top edge (y2): Stop covering x-range [x1, x2]
- For each rectangle, create two events:
- Coordinate Compression:
- Collect all unique x-coordinates
- Map them to indices for efficient segment tracking
- This handles large coordinate values (up to 10^9)
- Sweep Line:
- Process events from bottom to top (sorted by y)
- At each event, calculate the covered area since the last event
- Area = coveredLength × (currentY - previousY)
- Coverage Tracking:
count[i]tracks how many rectangles cover the segment [xCoords[i], xCoords[i+1]]- OPEN events increment count, CLOSE events decrement
Key Insight
The sweep line moves vertically (along y-axis), and at each position, we calculate the horizontal coverage. By processing events in order, we can compute the total area incrementally.
Example Walkthrough
Input: rectangles = [[0,0,2,2],[1,0,2,3],[1,0,3,1]]
Coordinate Compression:
X-coordinates: [0, 1, 2, 3]
Indices: [0, 1, 2, 3]
Events (sorted by y):
y=0: OPEN [0,2] (rect 0)
y=0: OPEN [1,2] (rect 1)
y=0: OPEN [1,3] (rect 2)
y=1: CLOSE [1,3] (rect 2)
y=2: CLOSE [0,2] (rect 0)
y=3: CLOSE [1,2] (rect 1)
Processing:
y=0 to y=1:
Coverage: [0,1]=1, [1,2]=3, [2,3]=1
Length: (1-0) + (2-1) + (3-2) = 3
Area: 3 × (1-0) = 3
y=1 to y=2:
After CLOSE [1,3]: [0,1]=1, [1,2]=2, [2,3]=0
Length: (1-0) + (2-1) = 2
Area: 2 × (2-1) = 2
y=2 to y=3:
After CLOSE [0,2]: [0,1]=0, [1,2]=1, [2,3]=0
Length: (2-1) = 1
Area: 1 × (3-2) = 1
Total: 3 + 2 + 1 = 6
Complexity
| Solution | Time | Space | Notes | |———-|——|——-|——-| | Sweep Line + Array | O(n² log n) | O(n) | Simple, good for n ≤ 200 | | Sweep Line + Segment Tree | O(n log n) | O(n) | Optimal for large n |
Common Mistakes
- Single rectangle: Just return its area modulo MOD
- Large coordinates: Use coordinate compression
- Overlapping rectangles: Coverage counting handles duplicates
- Adjacent rectangles: Properly counted as separate areas
-
Large area: Result can exceed 10^18, must use modulo
- Integer overflow: Use
long longfor intermediate calculations - Modulo operations: Apply modulo correctly to avoid overflow
- Event ordering: Process OPEN before CLOSE at same y-coordinate
- Coordinate compression: Don’t forget to map back to actual lengths
- Segment tree indexing: Be careful with 0-based vs 1-based indexing
Optimization Tips
- Use segment tree: For O(log n) updates instead of O(n)
- Sort events once: Avoid repeated sorting
- Process same-y events together: Can batch updates
- Use
setfor deduplication: Automatically sorts and removes duplicates
Related Problems
- 218. The Skyline Problem - Similar sweep line approach
- 391. Perfect Rectangle - Rectangle coverage
- 836. Rectangle Overlap - Simpler overlap detection
- 223. Rectangle Area - Two rectangles only
Pattern Recognition
This problem demonstrates the “Sweep Line with Segment Tree” pattern:
1. Generate events (start/end of ranges)
2. Coordinate compress if needed
3. Sort events by sweep dimension
4. Use segment tree for efficient range updates
5. Calculate answer incrementally
Similar problems:
- Skyline Problem
- Interval Coverage
- Range Sum Queries with Updates
Real-World Applications
- Computer Graphics: Calculating visible areas
- GIS Systems: Computing land coverage
- Collision Detection: Finding overlapping regions
- Layout Algorithms: Determining occupied space
- Map Rendering: Tile coverage calculations
References
- LC 850: Rectangle Area II on LeetCode
- LeetCode Discuss — LC 850: Rectangle Area II
- LeetCode Editorial (may require premium)
Key Takeaways
- Sweep line: Convert 2D problem to 1D by sweeping along one axis
- Coordinate compression: Handle large coordinates by mapping to indices
- Event-based processing: Track when rectangles start/end coverage
- Coverage counting: Count overlapping rectangles instead of boolean coverage
- Segment tree: Efficient range updates and queries