diff --git a/problems/0836-rectangle-overlap/analysis.md b/problems/0836-rectangle-overlap/analysis.md new file mode 100644 index 0000000..28505af --- /dev/null +++ b/problems/0836-rectangle-overlap/analysis.md @@ -0,0 +1,65 @@ +# 0836. Rectangle Overlap + +[LeetCode Link](https://leetcode.com/problems/rectangle-overlap/) + +Difficulty: Easy +Topics: Math, Geometry +Acceptance Rate: 48.3% + +## Hints + +### Hint 1 + +Resist the urge to enumerate the geometric configurations ("rec2 is above rec1", "rec2 is to the left", "rec2 is nested inside", ...). That path has a dozen cases and you will miss one. This is a Math/Geometry problem where the win comes from finding a single formula, not from a case analysis. Ask yourself: is it easier to characterize *overlap*, or its opposite? + +### Hint 2 + +Axis-aligned rectangles are separable: the X dimension and the Y dimension are completely independent. A rectangle `[x1, y1, x2, y2]` is really the *product* of two intervals — `[x1, x2]` on the X axis and `[y1, y2]` on the Y axis. So the 2D question reduces to a 1D question asked twice: when do two intervals on a line share more than a single point? + +### Hint 3 + +Two intervals `[a1, a2]` and `[b1, b2]` fail to overlap exactly when one ends at or before the other starts: `a2 <= b1 || b2 <= a1`. Flip that with De Morgan and you get the positive condition `a1 < b2 && b1 < a2`. The rectangles overlap iff *both* projections overlap, so: + +``` +rec1[0] < rec2[2] && rec2[0] < rec1[2] && // X intervals overlap +rec1[1] < rec2[3] && rec2[1] < rec1[3] // Y intervals overlap +``` + +Note the strict `<`: that is precisely what makes touching edges and corners count as "no overlap". + +## Approach + +The key structural fact is **dimension separability**. An axis-aligned rectangle is the Cartesian product of an X interval and a Y interval, so the intersection of two rectangles is the Cartesian product of the two X-interval intersections and the two Y-interval intersections. The intersection area is positive if and only if *both* of those 1D intersections have positive length. One empty projection collapses the whole area to zero, no matter how generously the other dimension overlaps. + +So the algorithm is: + +1. Unpack `rec1 = [x1, y1, x2, y2]` into the X interval `[x1, x2]` and Y interval `[y1, y2]`; do the same for `rec2`. +2. Check the X projections for positive-length overlap: `rec1[0] < rec2[2] && rec2[0] < rec1[2]`. +3. Check the Y projections the same way: `rec1[1] < rec2[3] && rec2[1] < rec1[3]`. +4. Return the conjunction of the two. + +Why is `a1 < b2 && b1 < a2` the right 1D test? It is easiest to derive by negation. Two intervals on a line miss each other in exactly two ways: `a` finishes before `b` starts (`a2 <= b1`), or `b` finishes before `a` starts (`b2 <= a1`). There is no third way — this is the classic separating-axis argument in one dimension. Negating that disjunction gives `a2 > b1 && b2 > a1`, which is the overlap test. The `<=` in the failure case becomes a strict `<` in the success case, which encodes "touching is not overlapping" for free. + +An equivalent framing some people find more intuitive: the intersection interval is `[max(a1, b1), min(a2, b2)]`, and it is non-degenerate exactly when `max(a1, b1) < min(a2, b2)`. That expands to the same four comparisons. + +Walking through Example 1 with `rec1 = [0,0,2,2]`, `rec2 = [1,1,3,3]`: X projections are `[0,2]` and `[1,3]`, and `0 < 3 && 1 < 2` holds, so they overlap on a segment of length 1. Y projections are identical by symmetry and also overlap. Both dimensions pass, so the answer is `true` — the shared region is the unit square `[1,2] x [1,2]`. + +Example 2, `rec1 = [0,0,1,1]` and `rec2 = [1,0,2,1]`, is the instructive one. The Y projections are both `[0,1]` and overlap fully. But on X we have `[0,1]` and `[1,2]`: the test `rec2[0] < rec1[2]` is `1 < 1`, which is false. The rectangles share the vertical segment `x = 1` but that segment has zero width, so the area is zero and the answer is `false`. This is exactly the case that a naive `<=` would get wrong. + +Example 3, `rec1 = [0,0,1,1]` and `rec2 = [2,2,3,3]`, fails on both dimensions — the rectangles are diagonally separated — and short-circuits to `false` on the very first comparison. + +Honest note on difficulty: this problem is genuinely Easy once you see the separability insight, but the sub-50% acceptance rate is real and earned. Nearly all the failures come from two places — attempting case enumeration instead of finding the formula, and using `<=` where `<` is required. If you write the negation first and then flip it, both traps disappear. + +## Complexity Analysis + +Time Complexity: O(1) — a fixed four comparisons regardless of input, with short-circuit evaluation often doing fewer. +Space Complexity: O(1) — no allocation; the inputs are read in place. + +## Edge Cases + +- **Touching along an edge** (`[0,0,1,1]` and `[1,0,2,1]`): the projections meet at a single point in one dimension. This is the canonical off-by-one trap and the reason the comparisons must be strict `<` rather than `<=`. Zero-width intersection means zero area means no overlap. +- **Touching at exactly one corner** (`[0,0,1,1]` and `[1,1,2,2]`): *both* dimensions degenerate to a single shared point simultaneously. Still `false`, and the same strict inequality handles it without extra code. +- **Fully nested rectangles** (`[0,0,10,10]` containing `[2,2,3,3]`): a tempting hand-rolled case analysis based on corner containment can miss this, since neither rectangle's corners behave symmetrically. The interval formula handles containment with no special casing — `[2,3]` sits strictly inside `[0,10]` on both axes. +- **Identical rectangles**: overlap is total, so `true`. Worth checking that the strict inequalities do not accidentally reject it — they do not, because a valid rectangle has non-zero area and therefore `x1 < x2`. +- **Separated in one dimension only** (e.g. same X span, disjoint Y spans): a reminder that overlap requires the *conjunction*, not the disjunction, of the two projection tests. Getting this backwards is a common bug. +- **Negative and large coordinates** (down to `-10^9` and up to `10^9`): all four values can be negative, so do not assume a positive coordinate space. Go's `int` is 64-bit on all mainstream platforms, and since the solution only compares values and never adds or multiplies them, there is no overflow risk regardless. diff --git a/problems/0836-rectangle-overlap/problem.md b/problems/0836-rectangle-overlap/problem.md new file mode 100644 index 0000000..24e2ed3 --- /dev/null +++ b/problems/0836-rectangle-overlap/problem.md @@ -0,0 +1,56 @@ +--- +number: "0836" +frontend_id: "836" +title: "Rectangle Overlap" +slug: "rectangle-overlap" +difficulty: "Easy" +topics: + - "Math" + - "Geometry" +acceptance_rate: 4831.6 +is_premium: false +created_at: "2026-09-14T05:14:47.428611+00:00" +fetched_at: "2026-09-14T05:14:47.428611+00:00" +link: "https://leetcode.com/problems/rectangle-overlap/" +date: "2026-09-14" +--- + +# 0836. Rectangle Overlap + +An axis-aligned rectangle is represented as a list `[x1, y1, x2, y2]`, where `(x1, y1)` is the coordinate of its bottom-left corner, and `(x2, y2)` is the coordinate of its top-right corner. Its top and bottom edges are parallel to the X-axis, and its left and right edges are parallel to the Y-axis. + +Two rectangles overlap if the area of their intersection is **positive**. To be clear, two rectangles that only touch at the corner or edges do not overlap. + +Given two axis-aligned rectangles `rec1` and `rec2`, return `true` _if they overlap, otherwise return_`false`. + + + +**Example 1:** + + + **Input:** rec1 = [0,0,2,2], rec2 = [1,1,3,3] + **Output:** true + + +**Example 2:** + + + **Input:** rec1 = [0,0,1,1], rec2 = [1,0,2,1] + **Output:** false + + +**Example 3:** + + + **Input:** rec1 = [0,0,1,1], rec2 = [2,2,3,3] + **Output:** false + + + + +**Constraints:** + + * `rec1.length == 4` + * `rec2.length == 4` + * `-109 <= rec1[i], rec2[i] <= 109` + * `rec1` and `rec2` represent a valid rectangle with a non-zero area. diff --git a/problems/0836-rectangle-overlap/solution_daily_20260914.go b/problems/0836-rectangle-overlap/solution_daily_20260914.go new file mode 100644 index 0000000..200715f --- /dev/null +++ b/problems/0836-rectangle-overlap/solution_daily_20260914.go @@ -0,0 +1,21 @@ +package main + +// 0836. Rectangle Overlap +// +// An axis-aligned rectangle is the Cartesian product of an X interval and a Y +// interval, so the 2D problem separates into the same 1D question asked twice: +// do two intervals share more than a single point? +// +// Two intervals [a1, a2] and [b1, b2] fail to overlap exactly when one ends at +// or before the other starts (a2 <= b1 || b2 <= a1). Negating that gives the +// overlap test a1 < b2 && b1 < a2. The strict inequalities are what make edges +// and corners that merely touch count as "no overlap", since those produce a +// zero-length intersection. +// +// The rectangles overlap iff both projections overlap. O(1) time, O(1) space. +func isRectangleOverlap(rec1 []int, rec2 []int) bool { + // X projections: [rec1[0], rec1[2]] and [rec2[0], rec2[2]]. + // Y projections: [rec1[1], rec1[3]] and [rec2[1], rec2[3]]. + return rec1[0] < rec2[2] && rec2[0] < rec1[2] && + rec1[1] < rec2[3] && rec2[1] < rec1[3] +} diff --git a/problems/0836-rectangle-overlap/solution_daily_20260914_test.go b/problems/0836-rectangle-overlap/solution_daily_20260914_test.go new file mode 100644 index 0000000..faaa9b0 --- /dev/null +++ b/problems/0836-rectangle-overlap/solution_daily_20260914_test.go @@ -0,0 +1,116 @@ +package main + +import "testing" + +func TestIsRectangleOverlap(t *testing.T) { + tests := []struct { + name string + rec1 []int + rec2 []int + expected bool + }{ + { + name: "example 1: partially overlapping corner to corner", + rec1: []int{0, 0, 2, 2}, + rec2: []int{1, 1, 3, 3}, + expected: true, + }, + { + name: "example 2: sharing a vertical edge only", + rec1: []int{0, 0, 1, 1}, + rec2: []int{1, 0, 2, 1}, + expected: false, + }, + { + name: "example 3: diagonally separated, no contact", + rec1: []int{0, 0, 1, 1}, + rec2: []int{2, 2, 3, 3}, + expected: false, + }, + { + name: "edge case: touching at exactly one corner", + rec1: []int{0, 0, 1, 1}, + rec2: []int{1, 1, 2, 2}, + expected: false, + }, + { + name: "edge case: rec2 fully nested inside rec1", + rec1: []int{0, 0, 10, 10}, + rec2: []int{2, 2, 3, 3}, + expected: true, + }, + { + name: "edge case: rec1 fully nested inside rec2", + rec1: []int{4, 4, 5, 5}, + rec2: []int{-1, -1, 9, 9}, + expected: true, + }, + { + name: "edge case: identical rectangles overlap completely", + rec1: []int{-3, -3, 3, 3}, + rec2: []int{-3, -3, 3, 3}, + expected: true, + }, + { + name: "edge case: sharing a horizontal edge only", + rec1: []int{0, 0, 2, 1}, + rec2: []int{0, 1, 2, 2}, + expected: false, + }, + { + name: "edge case: x overlaps but y is disjoint", + rec1: []int{0, 0, 5, 1}, + rec2: []int{1, 4, 4, 6}, + expected: false, + }, + { + name: "edge case: y overlaps but x is disjoint", + rec1: []int{0, 0, 1, 5}, + rec2: []int{3, 1, 6, 4}, + expected: false, + }, + { + name: "edge case: crossing plus shape overlaps", + rec1: []int{-5, -1, 5, 1}, + rec2: []int{-1, -5, 1, 5}, + expected: true, + }, + { + name: "edge case: entirely negative coordinates overlapping", + rec1: []int{-9, -9, -5, -5}, + rec2: []int{-7, -7, -1, -1}, + expected: true, + }, + { + name: "edge case: extreme coordinate bounds overlapping", + rec1: []int{-1000000000, -1000000000, 0, 0}, + rec2: []int{-1, -1, 1000000000, 1000000000}, + expected: true, + }, + { + name: "edge case: extreme coordinate bounds touching at origin", + rec1: []int{-1000000000, -1000000000, 0, 0}, + rec2: []int{0, 0, 1000000000, 1000000000}, + expected: false, + }, + { + name: "edge case: overlap is a thin sliver of positive area", + rec1: []int{0, 0, 10, 10}, + rec2: []int{9, -5, 20, 15}, + expected: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := isRectangleOverlap(tt.rec1, tt.rec2); got != tt.expected { + t.Errorf("isRectangleOverlap(%v, %v) = %v, want %v", tt.rec1, tt.rec2, got, tt.expected) + } + + // Overlap is symmetric: swapping the arguments must not change the result. + if got := isRectangleOverlap(tt.rec2, tt.rec1); got != tt.expected { + t.Errorf("isRectangleOverlap(%v, %v) = %v, want %v (symmetry)", tt.rec2, tt.rec1, got, tt.expected) + } + }) + } +}