Facebook Pixel

593. Valid Square

MediumGeometryMath
Leetcode Link

Problem Description

You are given four points in a 2D coordinate plane. Your task is to determine whether these four points form a valid square.

Each point is represented as [x, y] where x and y are the coordinates. The four points can be given in any order - they are not necessarily provided in a sequence that traces the perimeter of the square.

For the four points to form a valid square, they must satisfy two conditions:

  1. All four sides must have equal length (and the length must be positive, meaning the points cannot all be the same)
  2. All four angles must be 90 degrees (right angles)

The solution uses a helper function check(a, b, c) that verifies if three points form a right angle. It calculates the squared distances between all pairs of the three points:

  • d1 = squared distance between points a and b
  • d2 = squared distance between points a and c
  • d3 = squared distance between points b and c

For three points to form a right angle, two of the distances must be equal (these are the two sides of the right angle), and by the Pythagorean theorem, their sum must equal the third distance (the hypotenuse). The function checks all three possible configurations where different pairs could be the equal sides.

The main solution verifies that all four possible combinations of three points from the four given points form right angles. If all four combinations satisfy the right angle property, and the distances are non-zero, then the four points form a valid square.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

When we think about what makes a square unique among quadrilaterals, we realize that a square has a very special property: every corner is a right angle, and all sides are equal. This gives us a key insight - if we pick any three vertices of a square, they must form a right-angled triangle (specifically, an isosceles right triangle).

The breakthrough comes from recognizing that we don't need to identify which points are adjacent or diagonal to each other. Instead, we can leverage the fact that in a square, any group of three vertices will always form a right angle at one of the vertices.

To understand why this works, imagine a square ABCD. If we pick any three points, say A, B, and C:

  • If B is the corner with the right angle, then AB and BC are the two equal sides of the square, and AC is the diagonal
  • The Pythagorean theorem tells us that AB² + BC² = AC²

This relationship holds for every possible combination of three vertices from the square. What's clever about this approach is that it naturally handles the unordered input - we don't need to figure out which point connects to which.

The key mathematical insight is using squared distances instead of actual distances. This avoids dealing with square roots and floating-point precision issues. When checking if three points form a right angle, we look for two equal squared distances (the sides) whose sum equals the third squared distance (the hypotenuse).

By verifying that all four possible combinations of three points satisfy this right-angle property, we ensure that:

  1. All corners are 90 degrees (since every combination of three points forms a right angle)
  2. All sides are equal (since the equal pairs of distances in each check must be consistent across all checks)

The additional check for non-zero distances (and d1, and d2, etc.) ensures that we don't have duplicate points, which would give us zero distances and a false positive.

Learn more about Math patterns.

Solution Approach

The implementation uses a geometric validation approach based on distance calculations and the Pythagorean theorem.

Core Helper Function - check(a, b, c):

The helper function takes three points and verifies if they form a right angle. Here's how it works:

  1. Extract coordinates: Unpack the three points into their x and y coordinates:

    (x1, y1), (x2, y2), (x3, y3) = a, b, c
  2. Calculate squared distances: Compute the squared distance between each pair of points:

    • d1 = (x1 - x2)² + (y1 - y2)² - squared distance between points a and b
    • d2 = (x1 - x3)² + (y1 - y3)² - squared distance between points a and c
    • d3 = (x2 - x3)² + (y2 - y3)² - squared distance between points b and c
  3. Verify right angle conditions: Check if any configuration forms a right angle by testing three cases:

    • Case 1: d1 == d2 and d1 + d2 == d3 and d1 - Right angle at point a
    • Case 2: d2 == d3 and d2 + d3 == d1 and d2 - Right angle at point b
    • Case 3: d1 == d3 and d1 + d3 == d2 and d1 - Right angle at point c

    Each condition checks:

    • Two distances are equal (the two sides of the square meeting at the right angle)
    • Their sum equals the third distance (Pythagorean theorem for the diagonal)
    • The distance is non-zero (ensuring distinct points)

Main Validation Logic:

The main function validates that all four possible combinations of three points from the given four points form right angles:

return (
    check(p1, p2, p3)  # Check if p1, p2, p3 form a right angle
    and check(p2, p3, p4)  # Check if p2, p3, p4 form a right angle
    and check(p1, p3, p4)  # Check if p1, p3, p4 form a right angle
    and check(p1, p2, p4)  # Check if p1, p2, p4 form a right angle
)

If all four checks pass, it confirms that:

  • Every corner of the quadrilateral is a right angle (90 degrees)
  • All sides have equal length (enforced by the consistency of equal distances across all checks)
  • The points are distinct (non-zero distance checks)

Why This Works:

In a valid square, any three vertices will always form an isosceles right triangle. By ensuring all four possible triplets of vertices satisfy this property, we guarantee that the four points form a square. The algorithm elegantly handles the unordered input without needing to determine the connectivity or sequence of the points.

Time Complexity: O(1) - We perform a fixed number of operations regardless of input Space Complexity: O(1) - We only use a constant amount of extra space

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through an example with four points: p1 = [0, 0], p2 = [0, 1], p3 = [1, 1], p4 = [1, 0]

These points form a unit square with corners at the origin.

Step 1: Check first triplet (p1, p2, p3)

  • Points: [0, 0], [0, 1], [1, 1]
  • Calculate squared distances:
    • d1 = (0-0)² + (0-1)² = 1 (between p1 and p2)
    • d2 = (0-1)² + (0-1)² = 2 (between p1 and p3)
    • d3 = (0-1)² + (1-1)² = 1 (between p2 and p3)
  • Check conditions:
    • d1 == d3 ✓ (both equal 1)
    • d1 + d3 == d2 ✓ (1 + 1 = 2)
    • d1 > 0
  • This forms a right angle at p2

Step 2: Check second triplet (p2, p3, p4)

  • Points: [0, 1], [1, 1], [1, 0]
  • Calculate squared distances:
    • d1 = (0-1)² + (1-1)² = 1 (between p2 and p3)
    • d2 = (0-1)² + (1-0)² = 2 (between p2 and p4)
    • d3 = (1-1)² + (1-0)² = 1 (between p3 and p4)
  • Check conditions:
    • d1 == d3 ✓ (both equal 1)
    • d1 + d3 == d2 ✓ (1 + 1 = 2)
    • d1 > 0
  • This forms a right angle at p3

Step 3: Check third triplet (p1, p3, p4)

  • Points: [0, 0], [1, 1], [1, 0]
  • Calculate squared distances:
    • d1 = (0-1)² + (0-1)² = 2 (between p1 and p3)
    • d2 = (0-1)² + (0-0)² = 1 (between p1 and p4)
    • d3 = (1-1)² + (1-0)² = 1 (between p3 and p4)
  • Check conditions:
    • d2 == d3 ✓ (both equal 1)
    • d2 + d3 == d1 ✓ (1 + 1 = 2)
    • d2 > 0
  • This forms a right angle at p4

Step 4: Check fourth triplet (p1, p2, p4)

  • Points: [0, 0], [0, 1], [1, 0]
  • Calculate squared distances:
    • d1 = (0-0)² + (0-1)² = 1 (between p1 and p2)
    • d2 = (0-1)² + (0-0)² = 1 (between p1 and p4)
    • d3 = (0-1)² + (1-0)² = 2 (between p2 and p4)
  • Check conditions:
    • d1 == d2 ✓ (both equal 1)
    • d1 + d2 == d3 ✓ (1 + 1 = 2)
    • d1 > 0
  • This forms a right angle at p1

Result: All four triplet checks pass, confirming these four points form a valid square.

Notice how each check identified a different corner with the right angle (p2, p3, p4, and p1 respectively), and in each case, the two equal distances were the sides of the square (length 1) while the longer distance was the diagonal (length √2, or 2 when squared).

Solution Implementation

1class Solution:
2    def validSquare(
3        self, p1: List[int], p2: List[int], p3: List[int], p4: List[int]
4    ) -> bool:
5        """
6        Determines if four given points form a valid square.
7        A valid square has four equal sides and four right angles.
8      
9        Args:
10            p1, p2, p3, p4: Four points represented as [x, y] coordinates
11          
12        Returns:
13            True if the four points form a valid square, False otherwise
14        """
15      
16        def is_right_isosceles_triangle(point_a: List[int], point_b: List[int], point_c: List[int]) -> bool:
17            """
18            Checks if three points form a right isosceles triangle.
19            This occurs when two sides have equal length and the third side
20            satisfies the Pythagorean theorem.
21          
22            Args:
23                point_a, point_b, point_c: Three points as [x, y] coordinates
24              
25            Returns:
26                True if points form a right isosceles triangle with non-zero sides
27            """
28            # Extract coordinates for readability
29            x1, y1 = point_a[0], point_a[1]
30            x2, y2 = point_b[0], point_b[1]
31            x3, y3 = point_c[0], point_c[1]
32          
33            # Calculate squared distances between each pair of points
34            # Using squared distances to avoid floating point operations
35            dist_squared_ab = (x1 - x2) ** 2 + (y1 - y2) ** 2
36            dist_squared_ac = (x1 - x3) ** 2 + (y1 - y3) ** 2
37            dist_squared_bc = (x2 - x3) ** 2 + (y2 - y3) ** 2
38          
39            # Check if any configuration forms a right isosceles triangle:
40            # Two sides must be equal (isosceles) and satisfy Pythagorean theorem (right angle)
41            # Also ensure sides have non-zero length (last condition in each case)
42            return any([
43                # Case 1: AB == AC, and AB² + AC² == BC² (right angle at A)
44                dist_squared_ab == dist_squared_ac and 
45                dist_squared_ab + dist_squared_ac == dist_squared_bc and 
46                dist_squared_ab > 0,
47              
48                # Case 2: AC == BC, and AC² + BC² == AB² (right angle at C)
49                dist_squared_ac == dist_squared_bc and 
50                dist_squared_ac + dist_squared_bc == dist_squared_ab and 
51                dist_squared_ac > 0,
52              
53                # Case 3: AB == BC, and AB² + BC² == AC² (right angle at B)
54                dist_squared_ab == dist_squared_bc and 
55                dist_squared_ab + dist_squared_bc == dist_squared_ac and 
56                dist_squared_ab > 0,
57            ])
58      
59        # For four points to form a square, every combination of three points
60        # must form a right isosceles triangle (the fourth point being the opposite corner)
61        return (
62            is_right_isosceles_triangle(p1, p2, p3) and
63            is_right_isosceles_triangle(p2, p3, p4) and
64            is_right_isosceles_triangle(p1, p3, p4) and
65            is_right_isosceles_triangle(p1, p2, p4)
66        )
67
1class Solution {
2    /**
3     * Determines if four points form a valid square.
4     * A valid square has all four sides of equal length and all four angles are right angles.
5     * 
6     * @param p1 First point coordinates [x, y]
7     * @param p2 Second point coordinates [x, y]
8     * @param p3 Third point coordinates [x, y]
9     * @param p4 Fourth point coordinates [x, y]
10     * @return true if the four points form a valid square, false otherwise
11     */
12    public boolean validSquare(int[] p1, int[] p2, int[] p3, int[] p4) {
13        // Check if all possible combinations of three points form right triangles
14        // For a valid square, any three vertices should form a right isosceles triangle
15        return checkRightIsoscelesTriangle(p1, p2, p3) && 
16               checkRightIsoscelesTriangle(p1, p3, p4) && 
17               checkRightIsoscelesTriangle(p1, p2, p4) && 
18               checkRightIsoscelesTriangle(p2, p3, p4);
19    }
20
21    /**
22     * Checks if three points form a right isosceles triangle.
23     * Uses the Pythagorean theorem: for a right triangle, the sum of squares 
24     * of two shorter sides equals the square of the longest side.
25     * 
26     * @param pointA First point coordinates [x, y]
27     * @param pointB Second point coordinates [x, y]
28     * @param pointC Third point coordinates [x, y]
29     * @return true if the three points form a right isosceles triangle, false otherwise
30     */
31    private boolean checkRightIsoscelesTriangle(int[] pointA, int[] pointB, int[] pointC) {
32        // Extract coordinates for better readability
33        int xA = pointA[0], yA = pointA[1];
34        int xB = pointB[0], yB = pointB[1];
35        int xC = pointC[0], yC = pointC[1];
36      
37        // Calculate squared distances between each pair of points
38        // Using squared distances to avoid floating point operations
39        int distanceSquaredAB = (xA - xB) * (xA - xB) + (yA - yB) * (yA - yB);
40        int distanceSquaredAC = (xA - xC) * (xA - xC) + (yA - yC) * (yA - yC);
41        int distanceSquaredBC = (xB - xC) * (xB - xC) + (yB - yC) * (yB - yC);
42      
43        // Check if the triangle is right isosceles (two equal sides and satisfies Pythagorean theorem)
44        // Case 1: AB and AC are equal sides, BC is hypotenuse
45        if (distanceSquaredAB == distanceSquaredAC && 
46            distanceSquaredAB + distanceSquaredAC == distanceSquaredBC && 
47            distanceSquaredAB > 0) {
48            return true;
49        }
50      
51        // Case 2: AB and BC are equal sides, AC is hypotenuse
52        if (distanceSquaredAB == distanceSquaredBC && 
53            distanceSquaredAB + distanceSquaredBC == distanceSquaredAC && 
54            distanceSquaredAB > 0) {
55            return true;
56        }
57      
58        // Case 3: AC and BC are equal sides, AB is hypotenuse
59        if (distanceSquaredAC == distanceSquaredBC && 
60            distanceSquaredAC + distanceSquaredBC == distanceSquaredAB && 
61            distanceSquaredAC > 0) {
62            return true;
63        }
64      
65        // Not a right isosceles triangle
66        return false;
67    }
68}
69
1class Solution {
2public:
3    bool validSquare(vector<int>& p1, vector<int>& p2, vector<int>& p3, vector<int>& p4) {
4        // A valid square requires all four combinations of three points to form
5        // right isosceles triangles (two equal sides and a right angle)
6        return checkRightIsoscelesTriangle(p1, p2, p3) && 
7               checkRightIsoscelesTriangle(p1, p3, p4) && 
8               checkRightIsoscelesTriangle(p1, p2, p4) && 
9               checkRightIsoscelesTriangle(p2, p3, p4);
10    }
11
12private:
13    bool checkRightIsoscelesTriangle(vector<int>& pointA, vector<int>& pointB, vector<int>& pointC) {
14        // Extract coordinates for clarity
15        int x1 = pointA[0], y1 = pointA[1];
16        int x2 = pointB[0], y2 = pointB[1];
17        int x3 = pointC[0], y3 = pointC[1];
18      
19        // Calculate squared distances between all pairs of points
20        // Using squared distances to avoid floating point operations
21        int distSquaredAB = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
22        int distSquaredAC = (x1 - x3) * (x1 - x3) + (y1 - y3) * (y1 - y3);
23        int distSquaredBC = (x2 - x3) * (x2 - x3) + (y2 - y3) * (y2 - y3);
24      
25        // Check if the three points form a right isosceles triangle
26        // This requires two sides to be equal (isosceles) and satisfy Pythagorean theorem (right angle)
27        // Also ensure the distances are positive (non-zero) to avoid degenerate cases
28      
29        // Case 1: AB and AC are equal sides, BC is hypotenuse
30        if (distSquaredAB == distSquaredAC && distSquaredAB + distSquaredAC == distSquaredBC && distSquaredAB > 0) {
31            return true;
32        }
33      
34        // Case 2: AB and BC are equal sides, AC is hypotenuse
35        if (distSquaredAB == distSquaredBC && distSquaredAB + distSquaredBC == distSquaredAC && distSquaredAB > 0) {
36            return true;
37        }
38      
39        // Case 3: AC and BC are equal sides, AB is hypotenuse
40        if (distSquaredAC == distSquaredBC && distSquaredAC + distSquaredBC == distSquaredAB && distSquaredAC > 0) {
41            return true;
42        }
43      
44        return false;
45    }
46};
47
1function validSquare(p1: number[], p2: number[], p3: number[], p4: number[]): boolean {
2    // A valid square requires all four combinations of three points to form
3    // right isosceles triangles (two equal sides and a right angle)
4    return checkRightIsoscelesTriangle(p1, p2, p3) && 
5           checkRightIsoscelesTriangle(p1, p3, p4) && 
6           checkRightIsoscelesTriangle(p1, p2, p4) && 
7           checkRightIsoscelesTriangle(p2, p3, p4);
8}
9
10function checkRightIsoscelesTriangle(pointA: number[], pointB: number[], pointC: number[]): boolean {
11    // Extract coordinates for clarity
12    const x1 = pointA[0], y1 = pointA[1];
13    const x2 = pointB[0], y2 = pointB[1];
14    const x3 = pointC[0], y3 = pointC[1];
15  
16    // Calculate squared distances between all pairs of points
17    // Using squared distances to avoid floating point operations
18    const distSquaredAB = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
19    const distSquaredAC = (x1 - x3) * (x1 - x3) + (y1 - y3) * (y1 - y3);
20    const distSquaredBC = (x2 - x3) * (x2 - x3) + (y2 - y3) * (y2 - y3);
21  
22    // Check if the three points form a right isosceles triangle
23    // This requires two sides to be equal (isosceles) and satisfy Pythagorean theorem (right angle)
24    // Also ensure the distances are positive (non-zero) to avoid degenerate cases
25  
26    // Case 1: AB and AC are equal sides, BC is hypotenuse
27    if (distSquaredAB === distSquaredAC && distSquaredAB + distSquaredAC === distSquaredBC && distSquaredAB > 0) {
28        return true;
29    }
30  
31    // Case 2: AB and BC are equal sides, AC is hypotenuse
32    if (distSquaredAB === distSquaredBC && distSquaredAB + distSquaredBC === distSquaredAC && distSquaredAB > 0) {
33        return true;
34    }
35  
36    // Case 3: AC and BC are equal sides, AB is hypotenuse
37    if (distSquaredAC === distSquaredBC && distSquaredAC + distSquaredBC === distSquaredAB && distSquaredAC > 0) {
38        return true;
39    }
40  
41    return false;
42}
43

Time and Space Complexity

Time Complexity: O(1)

The algorithm performs a fixed number of operations regardless of input:

  • The check function is called exactly 4 times
  • Each check function call:
    • Calculates 3 squared distances (d1, d2, d3), each requiring constant time operations
    • Evaluates 3 boolean conditions in the any() function
    • All arithmetic operations (subtraction, multiplication, addition, comparison) are O(1)
  • Total operations: 4 × (3 distance calculations + 3 condition checks) = constant number of operations

Since the number of points is fixed at 4 and all operations are arithmetic comparisons on fixed-size data, the overall time complexity is O(1).

Space Complexity: O(1)

The algorithm uses a constant amount of extra space:

  • The check function uses 3 local variables (d1, d2, d3) to store squared distances
  • Temporary variables for unpacking coordinates (x1, y1, x2, y2, x3, y3)
  • No recursive calls (so no additional stack space beyond function calls)
  • No data structures that grow with input size

All space usage is independent of the input size (which is fixed anyway), resulting in O(1) space complexity.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Floating Point Precision Issues

Pitfall: If you calculate actual distances using square roots instead of squared distances, you may encounter floating-point precision errors when comparing distances for equality.

# WRONG: Using actual distances with square root
import math
d1 = math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)
d2 = math.sqrt((x1 - x3) ** 2 + (y1 - y3) ** 2)
if d1 == d2:  # May fail due to floating point precision
    # ...

Solution: Always work with squared distances to avoid floating-point operations entirely. Integer arithmetic is exact when coordinates are integers.

# CORRECT: Using squared distances
d1 = (x1 - x2) ** 2 + (y1 - y2) ** 2
d2 = (x1 - x3) ** 2 + (y1 - y3) ** 2
if d1 == d2:  # Exact comparison with integers
    # ...

2. Forgetting to Check for Degenerate Cases

Pitfall: Not verifying that distances are non-zero, which would mean some points are identical.

# WRONG: Missing non-zero check
def check(a, b, c):
    # ... calculate distances ...
    return d1 == d2 and d1 + d2 == d3  # Could be true if all points are the same!

Solution: Always include a check that distances are positive to ensure points are distinct.

# CORRECT: Including non-zero validation
def check(a, b, c):
    # ... calculate distances ...
    return d1 == d2 and d1 + d2 == d3 and d1 > 0  # Ensures distinct points

3. Incorrect Pythagorean Theorem Application

Pitfall: Confusing which distances should be equal and which should be the hypotenuse. Remember that in a right isosceles triangle formed by three vertices of a square, the two equal sides are the square's edges, and the longer side is the diagonal.

# WRONG: Incorrect relationship between distances
if d1 == d3 and d1 == d2:  # This would mean all three distances are equal - not possible in a right triangle!
    return True

Solution: Correctly identify that two distances should be equal (the sides), and their sum should equal the third (the diagonal).

# CORRECT: Proper Pythagorean relationship
if d1 == d2 and d1 + d2 == d3 and d1 > 0:  # Two sides equal, sum equals hypotenuse
    return True

4. Assuming Points are Given in Order

Pitfall: Trying to check adjacent sides or assuming the points form a connected path around the square's perimeter.

# WRONG: Assuming p1->p2->p3->p4 forms the perimeter
side1 = distance(p1, p2)
side2 = distance(p2, p3)
side3 = distance(p3, p4)
side4 = distance(p4, p1)
return side1 == side2 == side3 == side4  # Won't work if points are unordered!

Solution: The provided approach correctly handles unordered points by checking all possible triplet combinations, which works regardless of point ordering.

5. Checking Only Partial Conditions

Pitfall: Verifying only that all sides are equal (rhombus condition) without checking angles, or only checking some triplets instead of all four.

# WRONG: Only checking three out of four triplets
return check(p1, p2, p3) and check(p2, p3, p4) and check(p1, p3, p4)
# Missing check(p1, p2, p4) - could be a non-square quadrilateral!

Solution: Always verify all four triplet combinations to ensure every corner forms a right angle.

# CORRECT: Checking all four combinations
return (check(p1, p2, p3) and check(p2, p3, p4) and 
        check(p1, p3, p4) and check(p1, p2, p4))
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

How does merge sort divide the problem into subproblems?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More