本文选自算法导论某一章节,并用高等数学知识对其进行总结
 线段
探讨的问题:
- 对于给定的两个有向线段 p0p1 和 p0p2 ,相对于他们的公共端点 p0 来说,P0p1 是否在 p0p2 的顺时针方向?
- 对于给定的两个线段 p0p1 和 p0p2  ,如果先沿着 p0p1 再沿着 p0p2   前进,那么在点 p1 处是否要向左转?
- 线段 p0p1 和 p0p2  是否相交?
 向量积
向量积是计算线段的核心方法。若仅考虑二维平面 (xOy),对于给定的向量 p1=(x1,y1,0) 和 p2=(x2,y2,0) ,则:
p1×p2=⎣⎡ix1x2jy1y2k00⎦⎤=[x1x2y1y2]⋅k=(x1y2−x2y1)⋅k
若将向量看作大小运算,则:
p1×p2=x1y2−x2y1=−p2×p1
- 若 p1×p2 为正,则相对于 原点(0,0) 来说, p1 位于 p2 的顺时针方向;
- 若 p1×p2 为负,则相对于 原点(0,0) 来说, p1 位于 p2 的逆时针方向;
- 若 p1×p2 为零,则表示出现边界情况,此时两个向量是共线的
为了确定相对于公共端点  p0 ,有向线段 p0p1 是在顺时针还是逆时针方向更靠近 p0p1,可以将 p0 作为原点简化问题:用 p1−p0 表示向量 p1′=(x1−x0,y1−y0) 和  p2′=(x2−x0,y2−y0),计算叉积:
(p1−p0)×(p2−p0)=(x1−x0)(y2−y0)−(x2−x0)(y1−y0)
如果叉积为正,那么 p0p1 位于 p0p2 的顺时针方向;反之亦然。
 确定连续线段是向左转还是向右转
利用叉积可以避免计算角度问题。方法如下:对于两条连续线段  p0p1 和 p0p2 ,计算叉积 (p2−p0)×(p1−p0) ,若结果为正,则  p0p2 在 p0p1 的顺时针方向,即在 p1 处右转;反之亦然;若叉积为0,意味着 p0,p1,p2 三者共线。
 判定两条线段是否相交
需要检查每条线段是否跨越了包含另一条线段是直线。
 
伪代码如下
 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 
 | #include<stdio.h>#include<stdlib.h>
 
 struct point{
 int x;
 int y;
 point(){}
 point(int _x,int _y):x(_x),y(_y){}
 };
 
 
 int direction(point p0,point p1,point p2){
 return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);
 }
 
 int min(int a,int b){ return a<b?a:b; }
 int max(int a,int b){ return a>b?a:b; }
 
 
 bool on_segment(point p0,point p1,point p2){
 if((min(p0.x,p1.x)<=p2.x<=max(p0.x,p1.x))
 &&(min(p0.y,p1.y)<=p2.y<=max(p0.y,p1.y))) return true;
 return false;
 }
 
 
 bool segment_intersect(point p1,point p2,point p3,point p4){
 int d1=direction(p1,p2,p4);
 int d2=direction(p1,p2,p3);
 int d3=direction(p3,p4,p1);
 int d4=direction(p3,p4,p2);
 printf("d1:%d\td2:%d\td3:%d\td4:%d\n",d1,d2,d3,d4);
 if(((d1<0&&d2>0)||(d1>0&&d2<0)) && ((d3<0&&d4>0)||(d3>0&&d4<0))) return true;
 else if(d1==0&&on_segment(p1,p2,p4))return true;
 else if(d2==0&&on_segment(p1,p2,p3))return true;
 else if(d3==0&&on_segment(p3,p4,p1))return true;
 else if(d4==0&&on_segment(p3,p4,p2))return true;
 return false;
 }
 
 int main(){
 point p1(1,1),p2(4,4);
 point p3(3,3),p4(4,0);
 if(segment_intersect(p1,p2,p3,p4))
 printf("线段相交...\n");
 return 0;
 }
 
 |