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

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

    &nbsp;

    **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`

    &nbsp;

    **Step 3: Return the result**

    - If all points pass the cross product check, return `true`

    &nbsp;

    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.
