questions C
This commit is contained in:
156
backend/data/questions/check-if-it-is-a-straight-line.yaml
Normal file
156
backend/data/questions/check-if-it-is-a-straight-line.yaml
Normal file
@@ -0,0 +1,156 @@
|
||||
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:
|
||||
- two-pointers
|
||||
|
||||
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.
|
||||
Reference in New Issue
Block a user