原题描述
最近在刷codetop上的腾讯算法题时,有一道题目就是判断一个点是否在三角形内,给定这个三角形的三个顶点的坐标和要判断的这个点的坐标。
思路方法
等面积法
思路
很容易想到的是,可以用等面积法。如下图,O点如果在三角形内的话,显然O与三角形任意两边所形成的三角形的面积之和等于大三角形ABC的面积之和,但如果点O在三角形外的话,这样的等式显然是不成立的。

利用这个性质可以判断点是否在三角形内,但严谨一点,点如果在边上也是满足这个等式的,所以在分别求取小三角形面积的时候,可以判断一下面积是否为零,为零则说明点在边上。根据三角形的三个顶点求取三角形面积可以使用海伦公式,假设a,b,c分别为三角形的三边长。计算公式如下。
p = a + b + c 2 p=\frac{a+b+c}{2} p=2a+b+c
S = p ∗ ( p − a ) ∗ ( p − b ) ∗ ( p − c ) S=\sqrt{p * (p-a) * (p-b) * (p-c)} S=p∗(p−a)∗(p−b)∗(p−c)
但是等面积法在程序实现上由于计算机处理浮点数的误差,在比较的时候不能判断两个面积是否相等,而是应该判断两个差的绝对值是否在一个很小的数范围内。但是牛客网的测试数据中,依然有部分数据无法正常通过。
代码
type Point struct {
X, Y float64
}
// 给定三角形的三个顶点,求取三角形面积
func getTriangleArea(triangle [3]Point) float64 {
edges := make([]float64, 3)
for i := 0; i < 3; i++ {
x1, y1 := triangle[i].X, triangle[i].Y
x2, y2 := triangle[(i + 1) % 3].X, triangle[(i + 1) % 3].Y
edges[i] = math.Sqrt(float64((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)))
}
p := (edges[0] + edges[1] + edges[2]) / 2
return math.Sqrt(p * (p - edges[0]) * (p - edges[1]) * (p - edges[2]))
}
func isPointInTri(triangle [3]Point, point Point) bool {
// 面积法
totalArea := getTriangleArea(triangle)
area := 0.0
for i := 0; i < 3; i++ {
tri := triangle // 数组的话,这里会拷贝一份,与切片不同
tri[i] = point
area1 := getTriangleArea(tri)
if area1 < 1e-5 {
// 点可能在边上
return false
}
area += area1
}
return math.Abs(totalArea - area) < 1e-5 // 浮点数的比较
}
复杂度分析
- 时间复杂度: O ( 1 ) O(1) O(1)
- 空间复杂度: O ( 1 ) O(1) O(1)
叉乘法
思路
注意观察上图,可以发现,如果点在三角形内,那么对于任意一条边来说,该点和三角形剩下的一个点一定出现在这条边的同一侧,如果点不在三角形内,那么一定存在一条边,使得三角形外的该点和三角形剩下的一个点出现在这条边的两侧。利用这个性质,也可以判断点是否在三角形内。
那么如何判断两个点是否在一条边的同一侧呢?这里可以利用叉乘的性质。如下图,对于边OB来说,点A和点C是否在同一侧,可以利用以下的式子的正负号来判断,如果为正,则在同一侧,否则不在同一侧。

R = ( O A ⃗ × O B ⃗ ) ∗ ( O C ⃗ × O B ⃗ ) R=(\vec{OA} \times \vec{OB}) * (\vec{OC} \times \vec{OB})

最低0.47元/天 解锁文章
3680

被折叠的 条评论
为什么被折叠?



