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