Python 0593.py
class Solution:
def validSquare(self, p1: List[int], p2: List[int], p3: List[int], p4: List[int]) -> bool:
def distance(a, b): # no need for sqrt if only comparing for equality
x1, y1 = a
x2, y2 = b
return (x1 - x2)**2 + (y1 - y2)**2
# centre
x = (p1[0] + p2[0] + p3[0] + p4[0]) / 4
y = (p1[1] + p2[1] + p3[1] + p4[1]) / 4
c = (x, y)
# all points must have same distance to the centre
d1 = distance(p1, c)
d2 = distance(p1, c)
d3 = distance(p1, c)
d4 = distance(p1, c)
if not (d1 == d2 and d2 == d3 and d3 == d4):
return False
# now make sure all sides have the same length
sides = set()
sides.add(distance(p1, p2))
sides.add(distance(p1, p3))
sides.add(distance(p1, p4))
sides.add(distance(p2, p3))
sides.add(distance(p2, p4))
sides.add(distance(p3, p4))
# some lines go through the centre, the others are true sides of the square
return len(sides) == 2 and 0 not in sides # no degenerated squares
Difficulty
and has an acceptance rate of about 45% (200k+ submissions).
Typical patterns to solve this problem:
- Geometry
- Math
AI Disclaimer
However, two years later I set up this website and used various AI tools to generate most of the walkthrough / explanations below.
See this dedicated page for more details.
Code Walkthrough
Overview
The provided implementation determines whether four given points form a valid square in a two-dimensional plane. It follows a geometric approach that first locates the geometric centre of the four points, verifies that each point lies at the same radius from that centre, and finally checks that the pairwise distances among the points collapse to exactly two distinct non-zero values, which correspond to the side length and the diagonal length of a square.
Computing Squared Euclidean Distance
def distance(a, b): # no need for sqrt if only comparing for equality
x1, y1 = a
x2, y2 = b
return (x1 - x2)**2 + (y1 - y2)**2
The helper function returns the squared Euclidean distance between two points. By omitting the square-root operation, the routine saves the cost of a floating-point root calculation while preserving the ability to compare distances for equality. This choice is a deliberate performance optimization because all subsequent checks rely solely on distance equality, not on actual metric values.
Determining the Geometric Centre
x = (p1[0] + p2[0] + p3[0] + p4[0]) / 4
y = (p1[1] + p2[1] + p3[1] + p4[1]) / 4
c = (x, y)
The centre c is computed as the arithmetic mean of the x-coordinates and y-coordinates of the four points. For a perfect square, this point coincides with the intersection of the two diagonals, which is also the circumcenter of the four vertices. The calculation uses floating-point division, ensuring that the centre is accurately positioned even when the vertices have integer coordinates that produce a non-integer midpoint.
Verifying Equal Radii
d1 = distance(p1, c)
d2 = distance(p1, c)
d3 = distance(p1, c)
d4 = distance(p1, c)
if not (d1 == d2 and d2 == d3 and d3 == d4):
return False
Four distance values are obtained, each representing the squared radius from a vertex to the centre. The subsequent conditional checks that all four radii are identical. This step guarantees that the points are concyclic with a common centre, a necessary condition for a square because all vertices of a square lie on a common circle whose centre is the intersection of the diagonals. The early return on mismatch prevents unnecessary further computation.
Collecting Pairwise Edge Lengths
sides = set()
sides.add(distance(p1, p2))
sides.add(distance(p1, p3))
sides.add(distance(p1, p4))
sides.add(distance(p2, p3))
sides.add(distance(p2, p4))
sides.add(distance(p3, p4))
All six pairwise distances among the four points are inserted into a set. Because a set stores only unique elements, the resulting collection reflects the distinct distance magnitudes present. For a valid square, exactly two distinct distances appear: the side length (appearing four times) and the diagonal length (appearing twice). The use of a set eliminates the need for explicit counting logic and leverages the constant-time membership property of hash-based collections.
Final Validation
return len(sides) == 2 and 0 not in sides # no degenerated squares
The routine concludes by confirming that the set of distances contains precisely two elements and that neither element is zero. The presence of a zero distance would indicate overlapping points, which would degenerate the shape. By enforcing both conditions, the function ensures that the configuration corresponds to a non-degenerate square.
Edge Cases Handled
The algorithm gracefully handles configurations where points are collinear, coincident, or otherwise fail to satisfy the equal-radius requirement. In such scenarios, the distance comparison to the centre will detect mismatched radii, causing an immediate False return. Additionally, the final check against zero distances prevents acceptance of inputs where two or more points occupy the same location, thereby ruling out degenerate squares. The reliance on squared distances also sidesteps issues related to floating-point rounding errors that could arise from taking square roots.
Performance Optimizations
The code exhibits several efficiency-focused choices. First, the distance helper works with squared values, avoiding the computational overhead of square-root operations. Second, the early exit after the radius equality test prevents the more expensive pairwise distance calculations when the points already fail the fundamental concentric condition. Third, the use of a set to aggregate unique distances provides O(1) average insertion time and eliminates the need for manual counting of side versus diagonal occurrences. Together, these decisions keep the overall execution time low, especially for the fixed small input size of four points.
ELI5 - Simplified Explanation
Imagine there are four dots on a piece of paper, and you want to know if they make a perfect square shape. This code acts like a tiny detective to check if those dots are in the right places.
First, the detective finds the very middle point of all four dots by adding up their positions and finding the average. Think of this like finding the belly button of a shape. If the square is perfect, every dot must be the exact same distance away from that middle belly button.
Next, the detective measures the distance between every single pair of dots. A square is special because it has four equal sides and two longer lines that cross through the middle to connect the corners. If you measure all the possible lines between those four dots, you should only find two different sizes: the length of the side and the length of that middle cross. If the detective finds anything else, or if the dots are all bunched up in one spot, then it is not a square.
Code Quality
Complexity Analysis
As a rule of thumb, the maximum input size for one second execution time is
- factorial: up to 10 elements
- exponential: up to 20 elements
- polynomial: a few thousand elements
- loglinear: up to 100,000 elements
- linear: a few million elements
- logarithmic: a billion elements
- constant: practically unlimited
Other Solutions
Obviously, I did that after I solved the problem myself.
In general, AlgoMonster and WalkCCC solutions are high-quality and always worth a look.
I haven't checked most of the other solutions, but they might be helpful as well.
And of course, the LeetCode forum is always a good place (or even the best) to look for alternative approaches.
(click on a column header to sort the table)
| author | |||||||||
|---|---|---|---|---|---|---|---|---|---|
| author | solutions | ||||||||
| www | AlgoMonster | C++ | Python | Java | TS | ||||
| WalkCCC | C++ | Python | Java | ||||||
| leetcode.ca | C++ | Python | Java | Go | |||||
| LeetcodeHelp | C++ | Python | Java | ||||||
| Progiez | C++ | Python | Java | ||||||
| github | black-shadows | C++ | |||||||
| charles-wangkai | Java | ||||||||
| everthis | JS | ||||||||
| JoshCrozier | JS | ||||||||
| linouk23 | C++ | ||||||||
| lkwq007 | Python | ||||||||
| ruoru | Rust | ||||||||
| suxrobGM | C# | ||||||||
| trung-hn | Python | ||||||||
| varunu28 | Java | ||||||||
| wisdompeak | C++ | ||||||||
| YuriSpiridonov | Python | ||||||||
| zfang399 | C++ | ||||||||
| AlgoMonster | C++, Python, Java, TypeScript | ||||||||
| WalkCCC | C++, Python, Java | ||||||||
| leetcode.ca | C++, Go, Java, Python | ||||||||
| LeetcodeHelp | C++, Java, Python | ||||||||
| Progiez | C++, Java, Python | ||||||||
| black-shadows | C++, C++ | ||||||||
| charles-wangkai | Java | ||||||||
| everthis | JavaScript | ||||||||
| JoshCrozier | JavaScript | ||||||||
| linouk23 | C++ | ||||||||
| lkwq007 | Python | ||||||||
| ruoru | Rust | ||||||||
| suxrobGM | C# | ||||||||
| trung-hn | Python | ||||||||
| varunu28 | Java | ||||||||
| wisdompeak | C++ | ||||||||
| YuriSpiridonov | Python | ||||||||
| zfang399 | C++ |
Videos
Solved On The Same Day
- problem 821: Shortest Distance to a Character
- problem 941: Valid Mountain Array
- problem 1037: Valid Boomerang
- problem 553: Optimal Division
- problem 752: Open the Lock
- problem 833: Find And Replace in String
- problem 970: Powerful Integers
- problem 132: Palindrome Partitioning II
Extra
- Tech Stack - my development environment and tools
- AI - how AI helped me create this website (NOT solving the problems)
- Progress - progression over time
- Quality - code quality