Off-by-one on range boundaries
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Break down a hard problem into reliable checkpoints, edge-case handling, and complexity trade-offs.
Given an array rectangles where rectangles[i] = [xi, yi, ai, bi] represents an axis-aligned rectangle. The bottom-left point of the rectangle is (xi, yi) and the top-right point of it is (ai, bi).
Return true if all the rectangles together form an exact cover of a rectangular region.
Example 1:
Input: rectangles = [[1,1,3,3],[3,1,4,2],[3,2,4,4],[1,3,2,4],[2,3,3,4]] Output: true Explanation: All 5 rectangles together form an exact cover of a rectangular region.
Example 2:
Input: rectangles = [[1,1,2,3],[1,3,2,4],[3,1,4,2],[3,2,4,4]] Output: false Explanation: Because there is a gap between the two rectangular regions.
Example 3:
Input: rectangles = [[1,1,3,3],[3,1,4,2],[1,3,2,4],[2,2,4,4]] Output: false Explanation: Because two of the rectangles overlap with each other.
Constraints:
1 <= rectangles.length <= 2 * 104rectangles[i].length == 4-105 <= xi < ai <= 105-105 <= yi < bi <= 105Problem summary: Given an array rectangles where rectangles[i] = [xi, yi, ai, bi] represents an axis-aligned rectangle. The bottom-left point of the rectangle is (xi, yi) and the top-right point of it is (ai, bi). Return true if all the rectangles together form an exact cover of a rectangular region.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Hash Map · Math
[[1,1,3,3],[3,1,4,2],[3,2,4,4],[1,3,2,4],[2,3,3,4]]
[[1,1,2,3],[1,3,2,4],[3,1,4,2],[3,2,4,4]]
[[1,1,3,3],[3,1,4,2],[1,3,2,4],[2,2,4,4]]
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #391: Perfect Rectangle
class Solution {
public boolean isRectangleCover(int[][] rectangles) {
long area = 0;
int minX = rectangles[0][0], minY = rectangles[0][1];
int maxX = rectangles[0][2], maxY = rectangles[0][3];
Map<Pair, Integer> cnt = new HashMap<>();
for (int[] r : rectangles) {
area += (r[2] - r[0]) * (r[3] - r[1]);
minX = Math.min(minX, r[0]);
minY = Math.min(minY, r[1]);
maxX = Math.max(maxX, r[2]);
maxY = Math.max(maxY, r[3]);
cnt.merge(new Pair(r[0], r[1]), 1, Integer::sum);
cnt.merge(new Pair(r[0], r[3]), 1, Integer::sum);
cnt.merge(new Pair(r[2], r[3]), 1, Integer::sum);
cnt.merge(new Pair(r[2], r[1]), 1, Integer::sum);
}
if (area != (long) (maxX - minX) * (maxY - minY)
|| cnt.getOrDefault(new Pair(minX, minY), 0) != 1
|| cnt.getOrDefault(new Pair(minX, maxY), 0) != 1
|| cnt.getOrDefault(new Pair(maxX, maxY), 0) != 1
|| cnt.getOrDefault(new Pair(maxX, minY), 0) != 1) {
return false;
}
cnt.remove(new Pair(minX, minY));
cnt.remove(new Pair(minX, maxY));
cnt.remove(new Pair(maxX, maxY));
cnt.remove(new Pair(maxX, minY));
return cnt.values().stream().allMatch(c -> c == 2 || c == 4);
}
private static class Pair {
final int first;
final int second;
Pair(int first, int second) {
this.first = first;
this.second = second;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Pair pair = (Pair) o;
return first == pair.first && second == pair.second;
}
@Override
public int hashCode() {
return Objects.hash(first, second);
}
}
}
// Accepted solution for LeetCode #391: Perfect Rectangle
type pair struct {
first int
second int
}
func isRectangleCover(rectangles [][]int) bool {
area := 0
minX, minY := rectangles[0][0], rectangles[0][1]
maxX, maxY := rectangles[0][2], rectangles[0][3]
cnt := make(map[pair]int)
for _, r := range rectangles {
area += (r[2] - r[0]) * (r[3] - r[1])
minX = min(minX, r[0])
minY = min(minY, r[1])
maxX = max(maxX, r[2])
maxY = max(maxY, r[3])
cnt[pair{r[0], r[1]}]++
cnt[pair{r[0], r[3]}]++
cnt[pair{r[2], r[3]}]++
cnt[pair{r[2], r[1]}]++
}
if area != (maxX-minX)*(maxY-minY) ||
cnt[pair{minX, minY}] != 1 ||
cnt[pair{minX, maxY}] != 1 ||
cnt[pair{maxX, maxY}] != 1 ||
cnt[pair{maxX, minY}] != 1 {
return false
}
delete(cnt, pair{minX, minY})
delete(cnt, pair{minX, maxY})
delete(cnt, pair{maxX, maxY})
delete(cnt, pair{maxX, minY})
for _, c := range cnt {
if c != 2 && c != 4 {
return false
}
}
return true
}
# Accepted solution for LeetCode #391: Perfect Rectangle
class Solution:
def isRectangleCover(self, rectangles: List[List[int]]) -> bool:
area = 0
minX, minY = rectangles[0][0], rectangles[0][1]
maxX, maxY = rectangles[0][2], rectangles[0][3]
cnt = defaultdict(int)
for r in rectangles:
area += (r[2] - r[0]) * (r[3] - r[1])
minX = min(minX, r[0])
minY = min(minY, r[1])
maxX = max(maxX, r[2])
maxY = max(maxY, r[3])
cnt[(r[0], r[1])] += 1
cnt[(r[0], r[3])] += 1
cnt[(r[2], r[3])] += 1
cnt[(r[2], r[1])] += 1
if (
area != (maxX - minX) * (maxY - minY)
or cnt[(minX, minY)] != 1
or cnt[(minX, maxY)] != 1
or cnt[(maxX, maxY)] != 1
or cnt[(maxX, minY)] != 1
):
return False
del cnt[(minX, minY)], cnt[(minX, maxY)], cnt[(maxX, maxY)], cnt[(maxX, minY)]
return all(c == 2 or c == 4 for c in cnt.values())
// Accepted solution for LeetCode #391: Perfect Rectangle
struct Solution;
use std::collections::HashMap;
impl Solution {
fn is_rectangle_cover(rectangles: Vec<Vec<i32>>) -> bool {
let x1 = rectangles.iter().map(|v| v[0]).min().unwrap();
let y1 = rectangles.iter().map(|v| v[1]).min().unwrap();
let x2 = rectangles.iter().map(|v| v[2]).max().unwrap();
let y2 = rectangles.iter().map(|v| v[3]).max().unwrap();
let area = (x2 - x1) * (y2 - y1);
let sum: i32 = rectangles
.iter()
.map(|v| (v[2] - v[0]) * (v[3] - v[1]))
.sum();
if sum != area {
return false;
}
let mut hm: HashMap<(i32, i32), usize> = HashMap::new();
for v in rectangles {
*hm.entry((v[0], v[1])).or_default() += 1;
*hm.entry((v[2], v[3])).or_default() += 1;
*hm.entry((v[0], v[3])).or_default() += 1;
*hm.entry((v[2], v[1])).or_default() += 1;
}
for (k, v) in hm {
if k == (x1, y1) || k == (x1, y2) || k == (x2, y1) || k == (x2, y2) {
if v != 1 {
return false;
}
} else {
if v % 2 != 0 {
return false;
}
}
}
true
}
}
#[test]
fn test() {
let rectangles = vec_vec_i32![
[1, 1, 3, 3],
[3, 1, 4, 2],
[3, 2, 4, 4],
[1, 3, 2, 4],
[2, 3, 3, 4]
];
let res = true;
assert_eq!(Solution::is_rectangle_cover(rectangles), res);
}
// Accepted solution for LeetCode #391: Perfect Rectangle
// Auto-generated TypeScript example from java.
function exampleSolution(): void {
}
// Reference (java):
// // Accepted solution for LeetCode #391: Perfect Rectangle
// class Solution {
// public boolean isRectangleCover(int[][] rectangles) {
// long area = 0;
// int minX = rectangles[0][0], minY = rectangles[0][1];
// int maxX = rectangles[0][2], maxY = rectangles[0][3];
// Map<Pair, Integer> cnt = new HashMap<>();
//
// for (int[] r : rectangles) {
// area += (r[2] - r[0]) * (r[3] - r[1]);
//
// minX = Math.min(minX, r[0]);
// minY = Math.min(minY, r[1]);
// maxX = Math.max(maxX, r[2]);
// maxY = Math.max(maxY, r[3]);
//
// cnt.merge(new Pair(r[0], r[1]), 1, Integer::sum);
// cnt.merge(new Pair(r[0], r[3]), 1, Integer::sum);
// cnt.merge(new Pair(r[2], r[3]), 1, Integer::sum);
// cnt.merge(new Pair(r[2], r[1]), 1, Integer::sum);
// }
//
// if (area != (long) (maxX - minX) * (maxY - minY)
// || cnt.getOrDefault(new Pair(minX, minY), 0) != 1
// || cnt.getOrDefault(new Pair(minX, maxY), 0) != 1
// || cnt.getOrDefault(new Pair(maxX, maxY), 0) != 1
// || cnt.getOrDefault(new Pair(maxX, minY), 0) != 1) {
// return false;
// }
//
// cnt.remove(new Pair(minX, minY));
// cnt.remove(new Pair(minX, maxY));
// cnt.remove(new Pair(maxX, maxY));
// cnt.remove(new Pair(maxX, minY));
//
// return cnt.values().stream().allMatch(c -> c == 2 || c == 4);
// }
//
// private static class Pair {
// final int first;
// final int second;
//
// Pair(int first, int second) {
// this.first = first;
// this.second = second;
// }
//
// @Override
// public boolean equals(Object o) {
// if (this == o) {
// return true;
// }
// if (o == null || getClass() != o.getClass()) {
// return false;
// }
// Pair pair = (Pair) o;
// return first == pair.first && second == pair.second;
// }
//
// @Override
// public int hashCode() {
// return Objects.hash(first, second);
// }
// }
// }
Use this to step through a reusable interview workflow for this problem.
Two nested loops check every pair or subarray. The outer loop fixes a starting point, the inner loop extends or searches. For n elements this gives up to n²/2 operations. No extra space, but the quadratic time is prohibitive for large inputs.
Most array problems have an O(n²) brute force (nested loops) and an O(n) optimal (single pass with clever state tracking). The key is identifying what information to maintain as you scan: a running max, a prefix sum, a hash map of seen values, or two pointers.
Review these before coding to avoid predictable interview regressions.
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Wrong move: Zero-count keys stay in map and break distinct/count constraints.
Usually fails on: Window/map size checks are consistently off by one.
Fix: Delete keys when count reaches zero.
Wrong move: Temporary multiplications exceed integer bounds.
Usually fails on: Large inputs wrap around unexpectedly.
Fix: Use wider types, modular arithmetic, or rearranged operations.