LeetCode 939 - Minimum Area Rectangle
The problem gives us a collection of distinct points on a 2D plane. Each point is represented as [x, y], where x is the horizontal coordinate and y is the vertical coordinate.
Difficulty: 🟡 Medium
Topics: Array, Hash Table, Math, Geometry, Sorting
Solution
Problem Understanding
The problem gives us a collection of distinct points on a 2D plane. Each point is represented as [x, y], where x is the horizontal coordinate and y is the vertical coordinate.
Our task is to determine the minimum possible area of an axis-aligned rectangle that can be formed using four of these points as corners. An axis-aligned rectangle means:
- Its sides must be parallel to the X-axis and Y-axis
- The rectangle cannot be rotated
- The four corners must all exist in the input
For a rectangle with corners:
(x1, y1)(x1, y2)(x2, y1)(x2, y2)
the area is:
$\text{Area} = |x_2 - x_1| \times |y_2 - y_1|$
The goal is to return the smallest such area among all valid rectangles. If no rectangle can be formed, we return 0.
The constraints are important:
- Up to
500points - Coordinates can be as large as
40000 - All points are unique
Since the number of points is relatively small, algorithms around O(n²) are reasonable, but brute-force approaches around O(n³) or O(n⁴) become too expensive.
Several edge cases are important:
- There may be fewer than four points, making rectangle formation impossible.
- Points may all lie on a single line.
- Multiple rectangles may exist, and we must return the smallest area.
- Large coordinate values require careful area calculation, though standard integer arithmetic is sufficient.
- Rectangles with width or height zero are impossible because all points are unique.
Approaches
Brute Force Approach
A straightforward solution is to examine every possible combination of four points and determine whether they form a valid rectangle.
For four points to form an axis-aligned rectangle:
- There must be exactly two distinct x-coordinates
- There must be exactly two distinct y-coordinates
- All four corner combinations must exist
This works because every valid rectangle in this problem must follow that structure.
However, checking every quadruple of points is extremely expensive. With n points, there are:
$\binom{n}{4}$
possible groups of four points.
For n = 500, this becomes far too large.
Key Insight for the Optimal Solution
An axis-aligned rectangle is completely determined by two diagonal points.
Suppose we choose two points:
(x1, y1)(x2, y2)
If:
x1 != x2y1 != y2
then these two points can serve as opposite corners of a rectangle.
The remaining two required corners would be:
(x1, y2)(x2, y1)
So instead of checking groups of four points, we can:
- Iterate over every pair of points
- Treat them as possible diagonals
- Check whether the other two corners exist
A hash set allows constant-time lookup for points, making this much more efficient.
Approach Comparison
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n⁴) | O(1) | Checks every combination of four points |
| Optimal | O(n²) | O(n) | Uses diagonal observation with hash set lookups |
Algorithm Walkthrough
Optimal Algorithm
- Store every point inside a hash set.
We need fast existence checks for arbitrary points. A hash set allows average O(1) lookup time.
Each point is stored as a tuple (x, y).
2. Initialize a variable to track the minimum area.
We can start with infinity so that any valid rectangle becomes the current best answer. 3. Iterate through every pair of points.
For each pair:
(x1, y1)(x2, y2)
we check whether they can form a rectangle diagonal. 4. Skip invalid diagonals.
If either:
x1 == x2y1 == y2
then the points lie on the same vertical or horizontal line, so they cannot form opposite corners of a rectangle. 5. Compute the other two required corners.
The rectangle would require:
(x1, y2)(x2, y1)
- Check whether both corners exist in the hash set.
If both points are present, then a valid rectangle exists. 7. Compute the rectangle area.
The area is:
$|x_2-x_1|\times|y_2-y_1|$
- Update the minimum area if the new rectangle is smaller.
- After processing all pairs:
- Return the minimum area if a rectangle was found
- Otherwise return
0
Why it works
Every axis-aligned rectangle has exactly two diagonal corners. By iterating through every pair of points, we guarantee that every possible rectangle diagonal is considered.
For any valid rectangle, the other two corners are uniquely determined. Because we check whether those corners exist in the hash set, every valid rectangle will eventually be discovered.
Since we compute the area for every valid rectangle and keep the minimum, the final answer must be the smallest possible rectangle area.
Python Solution
from typing import List
class Solution:
def minAreaRect(self, points: List[List[int]]) -> int:
point_set = set()
for x, y in points:
point_set.add((x, y))
min_area = float("inf")
n = len(points)
for i in range(n):
x1, y1 = points[i]
for j in range(i + 1, n):
x2, y2 = points[j]
# Cannot form a rectangle diagonal
if x1 == x2 or y1 == y2:
continue
# Check if the other two corners exist
if (x1, y2) in point_set and (x2, y1) in point_set:
area = abs(x2 - x1) * abs(y2 - y1)
min_area = min(min_area, area)
return 0 if min_area == float("inf") else min_area
The implementation begins by converting all points into a hash set. This is the key optimization because rectangle validation depends on fast point existence checks.
The nested loops iterate over every pair of points. Each pair is treated as a potential rectangle diagonal.
If the two points share the same x-coordinate or y-coordinate, they cannot form a diagonal of an axis-aligned rectangle, so the algorithm skips them immediately.
For valid diagonals, the code computes the two missing corners and checks whether both exist in the set. If they do, a rectangle exists and its area is computed.
The minimum area is updated whenever a smaller rectangle is found.
At the end, if no rectangle was discovered, the value remains infinity, so the algorithm returns 0.
Go Solution
package main
import "math"
func minAreaRect(points [][]int) int {
pointSet := make(map[[2]int]bool)
for _, point := range points {
x, y := point[0], point[1]
pointSet[[2]int{x, y}] = true
}
minArea := math.MaxInt32
n := len(points)
for i := 0; i < n; i++ {
x1, y1 := points[i][0], points[i][1]
for j := i + 1; j < n; j++ {
x2, y2 := points[j][0], points[j][1]
// Cannot form a rectangle diagonal
if x1 == x2 || y1 == y2 {
continue
}
// Check if the other two corners exist
if pointSet[[2]int{x1, y2}] &&
pointSet[[2]int{x2, y1}] {
area := abs(x2-x1) * abs(y2-y1)
if area < minArea {
minArea = area
}
}
}
}
if minArea == math.MaxInt32 {
return 0
}
return minArea
}
func abs(x int) int {
if x < 0 {
return -x
}
return x
}
The Go implementation follows the same logic as the Python version.
Because Go does not support tuples as map keys, the solution uses a fixed-size array [2]int to represent a point.
The minimum area is initialized using math.MaxInt32. If no rectangle is found, the function returns 0.
A helper abs function is included because Go does not provide an integer absolute value function in the standard library.
Worked Examples
Example 1
Input:
points = [[1,1],[1,3],[3,1],[3,3],[2,2]]
Step 1: Build Hash Set
| Point |
|---|---|
| (1,1) |
| (1,3) |
| (3,1) |
| (3,3) |
| (2,2) |
Step 2: Examine Point Pairs
Consider pair:
| Point A | Point B |
|---|---|
| (1,1) | (3,3) |
These can form a diagonal because:
- x-values differ
- y-values differ
Required corners:
| Required Corner |
|---|
| (1,3) |
| (3,1) |
Both exist in the set.
Area:
$|3-1|\times|3-1|=4$
Current minimum area becomes 4.
Now consider:
| Point A | Point B |
|---|---|
| (1,3) | (3,1) |
Required corners:
(1,1)(3,3)
Both exist.
Area is again 4.
The point (2,2) does not help form any rectangle.
Final answer:
4
Example 2
Input:
points = [[1,1],[1,3],[3,1],[3,3],[4,1],[4,3]]
Candidate Rectangles
Rectangle using x-values 1 and 3:
| Corners |
|---|
| (1,1) |
| (1,3) |
| (3,1) |
| (3,3) |
Area:
$(3-1)\times(3-1)=4$
Rectangle using x-values 3 and 4:
| Corners |
|---|
| (3,1) |
| (3,3) |
| (4,1) |
| (4,3) |
Area:
$(4-3)\times(3-1)=2$
The minimum area is 2.
Final answer:
2
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n²) | Every pair of points is examined once |
| Space | O(n) | Hash set stores all points |
The dominant operation is the nested loop over point pairs. With n points, there are approximately:
$\frac{n(n-1)}{2}$
pairs to examine.
Each lookup inside the hash set is average O(1), so the overall complexity remains O(n²).
The space complexity is O(n) because all points are stored in the hash set.
Test Cases
from typing import List
class Solution:
def minAreaRect(self, points: List[List[int]]) -> int:
point_set = set()
for x, y in points:
point_set.add((x, y))
min_area = float("inf")
n = len(points)
for i in range(n):
x1, y1 = points[i]
for j in range(i + 1, n):
x2, y2 = points[j]
if x1 == x2 or y1 == y2:
continue
if (x1, y2) in point_set and (x2, y1) in point_set:
area = abs(x2 - x1) * abs(y2 - y1)
min_area = min(min_area, area)
return 0 if min_area == float("inf") else min_area
solution = Solution()
assert solution.minAreaRect(
[[1,1],[1,3],[3,1],[3,3],[2,2]]
) == 4 # Basic rectangle example
assert solution.minAreaRect(
[[1,1],[1,3],[3,1],[3,3],[4,1],[4,3]]
) == 2 # Multiple rectangles, choose minimum
assert solution.minAreaRect(
[[1,1],[2,2],[3,3]]
) == 0 # No rectangle possible
assert solution.minAreaRect(
[[0,0],[0,1],[1,0],[1,1]]
) == 1 # Smallest possible rectangle
assert solution.minAreaRect(
[[0,0],[0,2],[3,0],[3,2]]
) == 6 # Single rectangle
assert solution.minAreaRect(
[[1,1],[1,4],[5,1],[5,4],[2,2],[3,3]]
) == 12 # Extra points should not interfere
assert solution.minAreaRect(
[[0,0],[0,100],[100,0],[100,100]]
) == 10000 # Large coordinates
assert solution.minAreaRect(
[[1,1]]
) == 0 # Too few points
assert solution.minAreaRect(
[[1,1],[1,2],[2,1]]
) == 0 # Missing fourth corner
assert solution.minAreaRect(
[[0,0],[0,3],[4,0],[4,3],[0,1],[4,1]]
) == 4 # Multiple vertical alignments
| Test | Why |
|---|---|
[[1,1],[1,3],[3,1],[3,3],[2,2]] |
Verifies standard rectangle detection |
[[1,1],[1,3],[3,1],[3,3],[4,1],[4,3]] |
Ensures smallest rectangle is selected |
[[1,1],[2,2],[3,3]] |
Tests no rectangle case |
[[0,0],[0,1],[1,0],[1,1]] |
Validates minimum possible area |
[[0,0],[0,2],[3,0],[3,2]] |
Tests a single large rectangle |
[[1,1],[1,4],[5,1],[5,4],[2,2],[3,3]] |
Confirms irrelevant points are ignored |
[[0,0],[0,100],[100,0],[100,100]] |
Tests large coordinate handling |
[[1,1]] |
Tests insufficient points |
[[1,1],[1,2],[2,1]] |
Tests missing corner scenario |
[[0,0],[0,3],[4,0],[4,3],[0,1],[4,1]] |
Tests multiple candidate rectangles |
Edge Cases
No Rectangle Exists
A common edge case occurs when the points never form four valid rectangle corners. For example:
[[1,1],[2,2],[3,3]]
All points lie diagonally, so no axis-aligned rectangle can exist.
A buggy implementation might incorrectly assume any four points can form a rectangle. This solution avoids that issue by explicitly checking whether the two required corners exist.
If no rectangle is ever found, min_area remains infinity and the algorithm correctly returns 0.
Points Share the Same Row or Column
Pairs of points that share the same x-coordinate or y-coordinate cannot serve as rectangle diagonals.
For example:
(1,1), (1,3)
These points form a vertical line segment, not a diagonal.
Without the condition:
if x1 == x2 or y1 == y2:
continue
the algorithm could incorrectly attempt to compute degenerate rectangles with zero area.
Multiple Rectangles Exist
The input may contain many valid rectangles, and the algorithm must return the smallest area.
For example:
[[1,1],[1,3],[3,1],[3,3],[4,1],[4,3]]
There are two valid rectangles:
- Area
4 - Area
2
A naive implementation might stop after finding the first rectangle. This solution instead evaluates every valid rectangle and continuously updates the minimum area, guaranteeing the correct result.