Problem 593

Abstract / Summary

Check if four given points form a square by verifying equal side lengths and right angles without using any specific method.

Note

The full problem description, including helpful examples, can be found on LeetCode.

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
30 lines | 1152 bytes | submitted October 7, 2023 | GitHub   26% comment ratio | tab size 4 | low complexity

Difficulty

This problem is ranked as
easy medium hard

and has an acceptance rate of about 45% (200k+ submissions).

Typical patterns to solve this problem:
  • Geometry
  • Math
I solved 663 easy, 1250 medium and 309 hard problems

AI Disclaimer

Every single line of code was written by me, 100% from scratch, between August 2023 and August 2024.
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.
LeetCode profile | GitHub repo

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.

written by openai gpt

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.

written by google gemini

Code Quality

Several LLMs ranked my code quality on a scale from 1 (very poor) to 10 (outstanding):
n = 27 | median = 4 | μ = 4.81 | σ = 1.87

Complexity Analysis

Time: constant
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
generated automatically

Other Solutions

I wrote a basic crawler to find other solutions to that problem.
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++
30 links | 18 authors | 8 languages

Videos

2 videos   did my crawler come up with invalid videos ? drop me a line.

Solved On The Same Day

3 easy 4 medium 1 hard
9 solutions   October 7, 2023

Extra

There are a few extra pages giving you some insights into my setup:
  • 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