尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

力扣1037题解析:向量叉积法高效判断三点共线

力扣1037题解析:向量叉积法高效判断三点共线 在算法刷题的路上我们常常会遇到一些看似简单、实则暗藏数学玄机的题目。力扣LeetCode第1037题“有效的回旋镖”就是这样一个典型。它不要求复杂的动态规划或图论知识却精准地考察了开发者对基础几何概念的理解和代码实现的严谨性。很多同学初次看到题目时可能会觉得“这不就是判断三点是否共线吗”但在实现时却容易在斜率计算、浮点数精度、边界条件上栽跟头。本文将为你彻底拆解这道题。无论你是刚开始刷题的新手还是想巩固基础算法的进阶者都能通过本文掌握问题本质理解“回旋镖”在题目中的几何定义。核心算法掌握多种判断三点共线的方法及其优劣。Python实现获得可直接运行、高效且鲁棒的代码。避坑指南深入分析浮点数精度、除零错误等常见陷阱。举一反三将本题的解题思路迁移到其他几何类算法题中。我们将从最基础的概念讲起逐步深入到代码实现和优化并提供完整的测试用例确保你不仅能做出这道题更能理解其背后的数学原理和编程技巧。1. 问题背景与核心概念1.1 什么是“有效的回旋镖”首先我们抛开“回旋镖”这个有点迷惑性的名字直接看题目的数学描述给定平面上三个点的坐标points [[x1, y1], [x2, y2], [x3, y3]]判断这三个点是否构成一个“回旋镖”。构成“回旋镖”的条件是这三个点互不相同且不在同一条直线上。换句话说题目要求我们判断给定的三个点是否构成一个三角形面积不为零。因为如果三点共线它们就无法形成一个有面积的图形自然也就不是“回旋镖”了。为什么叫“回旋镖”可以想象一下回旋镖的形状它通常有一个明显的拐角。如果三点共线那就成了一条“棍子”而不是能飞回来的“回旋镖”了。这个名字形象地表达了需要三点构成一个角度的要求。1.2 核心如何判断三点共线这是解决本题的关键。在平面几何中判断三个点(x1, y1),(x2, y2),(x3, y3)是否共线有几种等价的方法斜率法如果点1到点2的斜率等于点1到点3的斜率则三点共线。即(y2 - y1) / (x2 - x1) (y3 - y1) / (x3 - x1)。但需处理x坐标相等斜率不存在和浮点数精度问题。向量叉积法推荐计算向量(x2-x1, y2-y1)和(x3-x1, y3-y1)的叉积。在二维平面上叉积的绝对值表示由这两个向量张成的平行四边形的面积。如果叉积为0说明两个向量共线即三点共线。公式为(x2 - x1) * (y3 - y1) - (y2 - y1) * (x3 - x1) 0。面积法直接计算由三点构成的三角形的面积。如果面积为0则三点共线。面积公式可由叉积推导得出。方法对比与选择斜率法直观但涉及除法需要处理除零和浮点数比较容易出错。向量叉积法只涉及整数乘法和减法避免了除法和浮点数是最安全、最常用的解法。面积法本质与叉积法相同。在算法竞赛和面试中向量叉积法因其简洁性和高精度而被广泛采用。本文将重点讲解这种方法。2. 环境准备与解题思路2.1 解题环境编程语言Python 3.x。本题解完全使用Python实现利用其简洁的语法和原生的整数运算。核心工具无需任何第三方库纯数学计算。输入格式题目参数points是一个包含三个子列表的列表每个子列表包含两个整数[x, y]。输出格式返回一个布尔值True或False。True表示是有效的回旋镖三点不共线False则表示不是。2.2 算法思路拆解我们的目标是实现一个函数isBoomerang(points)。步骤分解输入提取从points列表中取出三个点的坐标分别赋给(x1, y1),(x2, y2),(x3, y3)。检查点是否重合虽然题目描述可能隐含了“互不相同”的条件但严谨的代码应该检查是否有任意两点坐标完全相同。如果存在重合点则无法构成回旋镖因为需要三个不同的点。计算向量叉积计算向量v1 (x2 - x1, y2 - y1)计算向量v2 (x3 - x1, y3 - y1)计算叉积cross_product v1[0] * v2[1] - v1[1] * v2[0]即(x2 - x1) * (y3 - y1) - (y2 - y1) * (x3 - x1)判断结果如果cross_product 0说明三点共线返回False。如果cross_product ! 0说明三点不共线返回True。为什么叉积为0就共线二维向量叉积a × b a.x * b.y - a.y * b.x的几何意义是它们张成的平行四边形的有向面积。当两个向量方向相同或相反时共线它们无法“张开”一个平行四边形因此面积为0。3. 核心代码实现与详解3.1 基础实现向量叉积法这是最直接、最推荐的实现方式。from typing import List class Solution: def isBoomerang(self, points: List[List[int]]) - bool: 判断三点是否构成有效的回旋镖。 :param points: 包含三个点坐标的列表例如 [[x1,y1],[x2,y2],[x3,y3]] :return: True 如果是回旋镖否则 False # 1. 解包三个点的坐标 x1, y1 points[0] x2, y2 points[1] x3, y3 points[2] # 2. (可选但推荐) 检查是否有重合的点 # 如果任意两点重合则肯定不是回旋镖 if (x1 x2 and y1 y2) or (x1 x3 and y1 y3) or (x2 x3 and y2 y3): return False # 3. 计算向量叉积 # 向量 v1 (x2 - x1, y2 - y1) # 向量 v2 (x3 - x1, y3 - y1) # 叉积 v1.x * v2.y - v1.y * v2.x cross_product (x2 - x1) * (y3 - y1) - (y2 - y1) * (x3 - x1) # 4. 判断 # 叉积为0表示三点共线或重合但重合已在上一步排除 return cross_product ! 0 # 测试代码 if __name__ __main__: solution Solution() # 测试用例1: 有效的回旋镖 print(solution.isBoomerang([[1,1],[2,3],[3,2]])) # 输出: True # 测试用例2: 三点共线 print(solution.isBoomerang([[1,1],[2,2],[3,3]])) # 输出: False # 测试用例3: 有重合点 print(solution.isBoomerang([[1,1],[2,2],[1,1]])) # 输出: False # 测试用例4: 另一个有效的回旋镖 print(solution.isBoomerang([[0,0],[0,2],[2,1]])) # 输出: True代码逐行解读函数定义使用类型注解List[List[int]]明确输入格式提高代码可读性。坐标解包直接使用索引和多重赋值清晰获取每个点的横纵坐标。重合点检查这是一个重要的防御性编程步骤。即使题目假设点互异加上检查能使代码更健壮防止意外输入。检查两两点对是否完全相等。叉积计算这是核心的一行代码。完全按照公式(x2-x1)*(y3-y1) - (y2-y1)*(x3-x1)计算。注意运算顺序乘法和减法都在整数域进行没有精度损失。返回判断cross_product ! 0为True时返回True是回旋镖为False时返回False不是回旋镖。3.2 斜率法实现对比与避坑为了理解为什么叉积法更优我们来看看斜率法的实现以及它可能遇到的问题。class SolutionSlope: def isBoomerang(self, points: List[List[int]]) - bool: x1, y1 points[0] x2, y2 points[1] x3, y3 points[2] # 检查重合点 if (x1 x2 and y1 y2) or (x1 x3 and y1 y3) or (x2 x3 and y2 y3): return False # 斜率法判断 (y2-y1)/(x2-x1) (y3-y1)/(x3-x1) # 为了避免除零错误交叉相乘转化为乘法判断 # 即判断 (y2 - y1) * (x3 - x1) (y3 - y1) * (x2 - x1) # 注意这种转化实际上就是叉积公式的另一种形式但这里我们用它来理解斜率比较 # 方法A使用浮点数除法不推荐 # 需要处理x2-x1或x3-x1为0的情况且浮点数比较需用容差 # if x2 - x1 0 or x3 - x1 0: # # 如果两个分母都为0则三点x坐标相同共线 # # 如果只有一个为0则斜率一个无穷大一个有限值不共线 # return not (x2 - x1 0 and x3 - x1 0) # slope1 (y2 - y1) / (x2 - x1) # slope2 (y3 - y1) / (x3 - x1) # return abs(slope1 - slope2) 1e-10 # 使用容差比较 # 方法B使用交叉相乘推荐本质是叉积 # 判断 (y2 - y1) * (x3 - x1) (y3 - y1) * (x2 - x1) # 如果相等则斜率相等或同时不存在三点共线。 # 注意当 (x2-x1) 和 (x3-x1) 都为0时等式两边都是0成立对应三点x坐标相同共线。 # 当其中一个为0而另一个不为0时等式一边为0另一边非0不成立对应不共线。 # 这个判断逻辑与叉积法 (x2-x1)*(y3-y1) (y2-y1)*(x3-x1) 是等价的。 return (y2 - y1) * (x3 - x1) ! (y3 - y1) * (x2 - x1) # 测试斜率法 if __name__ __main__: sol_slope SolutionSlope() print(sol_slope.isBoomerang([[1,1],[2,2],[3,3]])) # False print(sol_slope.isBoomerang([[1,1],[1,2],[2,1]])) # True (一点x相同另一点不同) print(sol_slope.isBoomerang([[1,1],[1,2],[1,3]])) # False (所有点x相同共线)斜率法的陷阱除零错误直接计算斜率(y2-y1)/(x2-x1)时如果x2 x1会导致除零异常。必须单独处理垂直线的情况。浮点数精度即使处理了除零浮点数除法的结果可能存在微小的舍入误差。直接比较slope1 slope2可能因为精度问题得到错误结果必须使用一个极小的容差值如1e-10进行比较这引入了额外的复杂性和不确定性。逻辑复杂需要分情况讨论两个斜率都存在、一个斜率不存在垂直、两个斜率都不存在所有点垂直排列。代码会变得冗长且容易出错。而交叉相乘法通过将等式(y2-y1)/(x2-x1) (y3-y1)/(x3-x1)两边同时乘以(x2-x1)(x3-x1)得到了(y2-y1)*(x3-x1) (y3-y1)*(x2-x1)。这个等式完美规避了除法并且你会发现将它移项后就是叉积公式(x2-x1)*(y3-y1) - (y3-y1)*(x2-x1) 0。所以最优的斜率法实现最终会收敛到叉积法。结论直接使用向量叉积公式是最简洁、最安全、最高效的方法。4. 完整实战从理解到提交让我们模拟一次完整的力扣解题过程包括思考、编码、测试和提交。4.1 题目重述与确认力扣 1037. 有效的回旋镖难度简单标签几何 数学 数组描述给定一个数组points其中points[i] [xi, yi]表示 X-Y 平面上的一个点。如果这些点构成一个回旋镖则返回true。 回旋镖定义为一组三个互不相同且不在一条直线上的点。4.2 编写最终解答代码在力扣的在线编辑器中我们通常只需要提交Solution类的isBoomerang方法。以下是准备好直接提交的代码from typing import List class Solution: def isBoomerang(self, points: List[List[int]]) - bool: # 提取三个点 (x1, y1), (x2, y2), (x3, y3) points # 检查是否有重合的点根据题目要求三点应互异此检查增强鲁棒性 # 如果两点重合则“三角形”退化为线段或点不是回旋镖 if (x1 x2 and y1 y2) or (x1 x3 and y1 y3) or (x2 x3 and y2 y3): return False # 核心计算向量 (x2-x1, y2-y1) 和 (x3-x1, y3-y1) 的叉积 # 叉积为0表示两向量共线即三点共线 # 叉积不为0表示三点不共线构成回旋镖 return (x2 - x1) * (y3 - y1) ! (y2 - y1) * (x3 - x1)提交说明这段代码完全符合题目要求。使用了元组解包(x1, y1), (x2, y2), (x3, y3) points更加简洁。包含了重合点检查虽然题目说“互不相同”但检查是一个好习惯。核心判断只有一行return (x2 - x1) * (y3 - y1) ! (y2 - y1) * (x3 - x1)清晰高效。4.3 本地测试用例设计在提交前设计全面的测试用例进行验证是必不可少的。一个好的测试集应覆盖明显的有效回旋镖。明显的三点共线水平、垂直、斜线。包含重合点的情况。坐标值较大或为负数的情况。浮点数不本题坐标是整数但算法本身也适用于浮点数需注意精度。def test_isBoomerang(): sol Solution() test_cases [ # (输入points, 期望输出, 用例描述) ([[1,1],[2,3],[3,2]], True, 普通有效回旋镖), ([[1,1],[2,2],[3,3]], False, 斜线共线), ([[0,0],[0,1],[0,2]], False, 垂直线共线), ([[5,5],[5,5],[5,5]], False, 三点完全重合), ([[0,0],[1,1],[0,0]], False, 两点重合), ([[0,0],[0,0],[1,1]], False, 另两点重合), ([[0,0],[1,0],[2,0]], False, 水平线共线), ([[0,0],[1,2],[2,4]], False, 另一条斜线共线), ([[0,0],[1,1],[2,0]], True, 构成等腰三角形), ([[-1,-1],[0,0],[1,1]], False, 负坐标共线), ([[-1,0],[0,1],[1,0]], True, 负坐标有效回旋镖), ([[10000,5000],[5000,10000],[15000,15000]], True, 大坐标有效), ([[0,0],[1,10000],[2,20000]], False, 大坐标共线), ] all_passed True for points, expected, desc in test_cases: result sol.isBoomerang(points) if result expected: print(f✓ 通过: {desc}) else: print(f✗ 失败: {desc}) print(f 输入: {points}) print(f 期望: {expected}, 实际: {result}) all_passed False if all_passed: print(\n所有测试用例通过) else: print(\n存在未通过的测试用例。) if __name__ __main__: test_isBoomerang()运行这段测试代码确保所有用例都通过再提交到力扣平台可以极大提高一次通过的信心。4.4 复杂度分析时间复杂度O(1)。只进行了固定次数的算术运算和比较与输入规模无关。空间复杂度O(1)。只使用了常数个额外变量。这是最优的复杂度无法再优化。5. 常见问题与排查思路在解决和实现这道题时你可能会遇到或想到以下问题5.1 为什么我的代码在某个测试用例上失败了问题现象可能原因解决方案返回True但预期False共线判断错误1. 使用了浮点数斜率比较且容差设置不当。2. 叉积计算逻辑写反了符号。3. 没有处理点重合的情况而重合点属于特殊的共线。1.改用叉积法彻底避免浮点数。2. 核对叉积公式(x2-x1)*(y3-y1) - (y2-y1)*(x3-x1)。3. 在计算叉积前先检查任意两点是否重合。返回False但预期True有效判断错误1. 坐标提取错误例如误用了points[0][0]和points[0][1]。2. 重合点检查逻辑过于严格错误地将不重合的点判为重合并返回False。1. 打印出提取的x1, y1, x2, y2, x3, y3确认是否正确。2. 检查重合点判断条件(x1x2 and y1y2)等确保逻辑是“或”关系。提交时“执行出错”如除零错误使用了直接的斜率除法且未检查分母为零。切换到叉积法或交叉相乘法它们天然避免了除法。5.2 叉积公式的符号重要吗对于本题“是否为零”的判断符号不重要。我们关心的是叉积的绝对值是否为零。cross_product 0和cross_product 0分别表示向量v2在v1的逆时针和顺时针方向但这不影响共线性判断。所以用! 0或 0判断即可。5.3 如果坐标是浮点数怎么办题目明确坐标是整数但假设我们遇到浮点数版本的类似问题叉积法依然是最佳选择因为它只涉及乘法和减法相比除法能减少一次精度损失。但是由于浮点数计算本身就有精度误差不能直接判断 0。需要判断叉积的绝对值是否小于一个极小的数如1e-10。def is_boomerang_float(points): x1, y1, x2, y2, x3, y3 points.flatten() # 假设是numpy数组或类似结构 cross (x2 - x1) * (y3 - y1) - (y2 - y1) * (x3 - x1) return abs(cross) 1e-10 # 大于容差则认为不共线容差值epsilon的选择需要根据数据范围和精度要求来定。5.4 这道题有更“高级”的解法吗对于三个点叉积法已经是时间复杂度 O(1)、空间复杂度 O(1) 的最优解。一些同学可能会想到海伦公式求面积但海伦公式需要开平方会引入不必要的浮点数运算和精度问题不如叉积法直接。6. 最佳实践与举一反三6.1 几何类算法题的通用技巧优先使用整数运算在条件允许时如本题坐标是整数尽量通过变形如交叉相乘避免浮点数除法和开方以保证精确性和效率。掌握向量运算点积、叉积是解决二维/三维几何问题的利器。叉积判共线/平行点积判垂直。注意精度问题浮点数比较必须使用容差abs(a - b) epsilon。epsilon的选择要合理通常取1e-9或1e-12。考虑退化情况点重合、线段长度为0、斜率为无穷大等边界情况往往是测试用例的重点也是代码容易出错的地方。画图辅助在纸上画出点的位置有助于直观理解问题验证算法逻辑。6.2 相关力扣题目推荐掌握了本题的叉积法你可以轻松解决一系列类似的几何基础题LeetCode 1232. 缀点成线判断一系列点是否都在同一条直线上。本题的扩展版核心依然是判断连续三点是否共线遍历即可。LeetCode 149. 直线上最多的点数困难题给定一组点找到一条直线上最多的点数。需要结合叉积判共线和哈希表进行统计。LeetCode 335. 路径交叉判断给定的移动路径是否自相交。涉及线段相交的判断需要用到更复杂的几何计算。LeetCode 939. 最小面积矩形找到能形成矩形的最小面积。需要利用矩形的几何性质对角点。6.3 工程化思考即使在简单的算法题中也能培养工程化思维防御性编程即使题目说“点互不相同”在函数开始处检查重合点能使你的函数更健壮避免因调用方传入意外数据而崩溃。代码可读性使用有意义的变量名如cross_product添加清晰的注释解释关键步骤如叉积为0的意义。测试驱动像我们上面做的那样先编写全面的测试用例再实现功能。这能帮你快速定位问题并对代码正确性有信心。复杂度意识即使对于小规模输入也要清楚算法的时间和空间复杂度这是评价解法优劣的核心指标之一。7. 总结力扣第1037题“有效的回旋镖”是一道优秀的几何入门题它巧妙地将数学概念转化为编程问题。通过这道题我们深入探讨了判断三点共线的多种方法并最终确立了向量叉积法作为最佳实践。关键收获问题本质判断三点是否共线等价于判断由它们构成的三角形面积是否为零。核心公式向量叉积(x2-x1)*(y3-y1) - (y2-y1)*(x3-x1)为0当且仅当三点共线。实现要点使用整数运算避免精度问题。可先检查点是否重合以增强鲁棒性。一行核心代码即可完成判断。避坑指南远离浮点数斜率比较警惕除零错误充分考虑点重合等边界情况。这道题的价值不仅在于其本身更在于它提供的解题范式——将几何问题转化为代数计算。这种思想在解决更复杂的计算几何问题时至关重要。下次当你遇到点、线、面的关系问题时不妨先想想能否用向量运算来简洁地描述它。现在你可以自信地将这份代码提交到力扣并尝试用同样的思路去挑战上面推荐的相关题目了。刷题路上理解远比死记硬背更重要。
返回列表