178 lines
8.2 KiB
YAML
178 lines
8.2 KiB
YAML
title: Check If It Is a Straight Line
|
|
slug: check-if-it-is-a-straight-line
|
|
difficulty: easy
|
|
leetcode_id: 1232
|
|
leetcode_url: https://leetcode.com/problems/check-if-it-is-a-straight-line/
|
|
categories:
|
|
- arrays
|
|
- math
|
|
patterns:
|
|
- slug: two-pointers
|
|
is_optimal: true
|
|
|
|
function_signature: "def check_straight_line(coordinates: list[list[int]]) -> bool:"
|
|
|
|
test_cases:
|
|
visible:
|
|
- input: { coordinates: [[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7]] }
|
|
expected: true
|
|
- input: { coordinates: [[1, 1], [2, 2], [3, 4], [4, 5], [5, 6], [7, 7]] }
|
|
expected: false
|
|
hidden:
|
|
- input: { coordinates: [[0, 0], [1, 1]] }
|
|
expected: true
|
|
- input: { coordinates: [[0, 0], [0, 1], [0, 2]] }
|
|
expected: true
|
|
- input: { coordinates: [[1, 0], [2, 0], [3, 0]] }
|
|
expected: true
|
|
- input: { coordinates: [[1, 1], [2, 2], [3, 3], [4, 4], [5, 5]] }
|
|
expected: true
|
|
- input: { coordinates: [[0, 0], [1, 1], [2, 3]] }
|
|
expected: false
|
|
|
|
description: |
|
|
You are given an array `coordinates`, where `coordinates[i] = [x, y]` represents the coordinate of a point. Check if these points make a straight line in the XY plane.
|
|
|
|
constraints: |
|
|
- `2 <= coordinates.length <= 1000`
|
|
- `coordinates[i].length == 2`
|
|
- `-10^4 <= coordinates[i][0], coordinates[i][1] <= 10^4`
|
|
- `coordinates` contains no duplicate points
|
|
|
|
examples:
|
|
- input: "coordinates = [[1,2],[2,3],[3,4],[4,5],[5,6],[6,7]]"
|
|
output: "true"
|
|
explanation: "All points lie on the line y = x + 1."
|
|
- input: "coordinates = [[1,1],[2,2],[3,4],[4,5],[5,6],[7,7]]"
|
|
output: "false"
|
|
explanation: "The point [3,4] does not lie on the line formed by [1,1] and [2,2]."
|
|
|
|
explanation:
|
|
intuition: |
|
|
Imagine drawing a line through the first two points on a graph. For all the remaining points to lie on that same line, they must all share the **same slope** relative to the starting point.
|
|
|
|
The slope between two points `(x1, y1)` and `(x2, y2)` is calculated as `(y2 - y1) / (x2 - x1)`. If every point has the same slope relative to the first point, they're all collinear (on the same straight line).
|
|
|
|
However, there's a catch: dividing by zero when `x2 - x1 = 0` (a vertical line) or dealing with floating-point precision issues. The elegant solution is to use **cross multiplication** to avoid division entirely.
|
|
|
|
Instead of comparing `(y2 - y1) / (x2 - x1) == (y3 - y1) / (x3 - x1)`, we rearrange to: `(y2 - y1) * (x3 - x1) == (y3 - y1) * (x2 - x1)`. This works for all cases, including vertical lines.
|
|
|
|
approach: |
|
|
We solve this using a **Cross Product** approach to check collinearity:
|
|
|
|
**Step 1: Get the reference direction**
|
|
|
|
- Calculate `dx`: the difference in x-coordinates between the first two points (`x1 - x0`)
|
|
- Calculate `dy`: the difference in y-coordinates between the first two points (`y1 - y0`)
|
|
- This gives us the "direction vector" of the line
|
|
|
|
|
|
|
|
**Step 2: Check each subsequent point**
|
|
|
|
- For each point from index `2` onwards, calculate its displacement from the first point
|
|
- Compute `dx_i = x_i - x0` and `dy_i = y_i - y0`
|
|
- Check if the cross product equals zero: `dy * dx_i == dx * dy_i`
|
|
- If not equal, the point is not on the line — return `false`
|
|
|
|
|
|
|
|
**Step 3: Return the result**
|
|
|
|
- If all points pass the cross product check, return `true`
|
|
|
|
|
|
|
|
The cross product check works because two vectors are parallel (and thus collinear with the origin) if and only if their cross product is zero.
|
|
|
|
common_pitfalls:
|
|
- title: Division-Based Slope Comparison
|
|
description: |
|
|
A naive approach might compute slopes using division:
|
|
```python
|
|
slope = (y2 - y1) / (x2 - x1)
|
|
```
|
|
This fails for vertical lines where `x2 - x1 = 0`, causing a division by zero error. Additionally, floating-point arithmetic can introduce precision errors when comparing slopes.
|
|
|
|
Using cross multiplication avoids both issues entirely.
|
|
wrong_approach: "Divide to get slope, compare with equality"
|
|
correct_approach: "Use cross product: dy * dx_i == dx * dy_i"
|
|
|
|
- title: Forgetting Edge Cases
|
|
description: |
|
|
With only 2 points, any two distinct points form a line. The algorithm handles this naturally since there are no additional points to check beyond the reference pair.
|
|
|
|
Some implementations might incorrectly return `false` for 2 points or fail to handle negative coordinates properly.
|
|
wrong_approach: "Special-case 2 points incorrectly"
|
|
correct_approach: "The general algorithm works for 2+ points"
|
|
|
|
- title: Using Wrong Reference Point
|
|
description: |
|
|
When checking collinearity, always compute displacements from a consistent reference point (typically the first point). Using different reference points for different comparisons can lead to incorrect results due to accumulated errors or logic mistakes.
|
|
wrong_approach: "Compare adjacent pairs independently"
|
|
correct_approach: "Compare all points to the same reference (first point)"
|
|
|
|
key_takeaways:
|
|
- "**Cross product for collinearity**: When checking if points are on the same line, use cross multiplication `(dy * dx_i == dx * dy_i)` to avoid division by zero and floating-point precision issues"
|
|
- "**Avoid division when possible**: In geometry problems, reformulating division as multiplication often leads to more robust solutions"
|
|
- "**Vector thinking**: Treat the difference between two points as a direction vector — this perspective simplifies many geometry problems"
|
|
- "**Foundation for line algorithms**: This technique extends to problems involving line detection, convex hulls, and computational geometry"
|
|
|
|
time_complexity: "O(n). We iterate through each point exactly once, where `n` is the number of points."
|
|
space_complexity: "O(1). We only use a constant number of variables (`dx`, `dy`, `dx_i`, `dy_i`) regardless of input size."
|
|
|
|
solutions:
|
|
- approach_name: Cross Product
|
|
is_optimal: true
|
|
code: |
|
|
def check_straight_line(coordinates: list[list[int]]) -> bool:
|
|
# Get the direction vector from first two points
|
|
x0, y0 = coordinates[0]
|
|
x1, y1 = coordinates[1]
|
|
dx = x1 - x0 # Change in x for reference direction
|
|
dy = y1 - y0 # Change in y for reference direction
|
|
|
|
# Check if each subsequent point lies on the same line
|
|
for i in range(2, len(coordinates)):
|
|
xi, yi = coordinates[i]
|
|
# Displacement from first point to current point
|
|
dx_i = xi - x0
|
|
dy_i = yi - y0
|
|
|
|
# Cross product check: if not zero, points aren't collinear
|
|
# This is equivalent to checking if slopes are equal
|
|
# but avoids division (and thus division by zero)
|
|
if dy * dx_i != dx * dy_i:
|
|
return False
|
|
|
|
return True
|
|
explanation: |
|
|
**Time Complexity:** O(n) — Single pass through the coordinates array.
|
|
|
|
**Space Complexity:** O(1) — Only uses a fixed number of variables.
|
|
|
|
We compute the reference direction from the first two points, then verify each subsequent point lies on the same line using the cross product. If `dy * dx_i != dx * dy_i` for any point, the vectors aren't parallel, meaning the points aren't collinear.
|
|
|
|
- approach_name: Slope Comparison (with edge case handling)
|
|
is_optimal: false
|
|
code: |
|
|
def check_straight_line(coordinates: list[list[int]]) -> bool:
|
|
x0, y0 = coordinates[0]
|
|
x1, y1 = coordinates[1]
|
|
|
|
for i in range(2, len(coordinates)):
|
|
xi, yi = coordinates[i]
|
|
# Check using cross multiplication to avoid division
|
|
# (y1 - y0) / (x1 - x0) == (yi - y0) / (xi - x0)
|
|
# becomes: (y1 - y0) * (xi - x0) == (yi - y0) * (x1 - x0)
|
|
if (y1 - y0) * (xi - x0) != (yi - y0) * (x1 - x0):
|
|
return False
|
|
|
|
return True
|
|
explanation: |
|
|
**Time Complexity:** O(n) — Single pass through the array.
|
|
|
|
**Space Complexity:** O(1) — Constant extra space.
|
|
|
|
This is mathematically equivalent to the cross product approach but written in a form that more clearly shows the slope comparison origin. Both avoid division by using cross multiplication.
|