普林斯顿公开课 算法2-6:凸包

凸包问题就是给定一堆点,求包含所有点的最小多边形。



应用


板上钉钉子



机器人最短路径

机器人要绕过障碍物,到达另一个点,最短的路径是什么


最远节点问题


算法

算法的基本思想就是先找到y坐标最小的点,再计算它与其它点的所成的角度,将其他的点按照角度进行排序。


依次连接各个点,如果出现大于等于180度的角,就说明该点在多边形里面,不是顶点,因此跳过该点。



如何找出y坐标最小的点

依次扫描所有的点,记录y坐标最小的点,记为点p。


如何按角度进行排序

分别计算每个点到p与x坐标正方向的夹角,再对浮点数进行排序


如何判断p1 p2 p3角度小于180度

CCW:给定三个点a b c,判断a->b->c是否为逆时针旋转

2×三角形的面积=叉乘的模=(bx-ax)(cy-ay)-(by-ay)(cx-ax)

如果面积大于0,则表示a->b->c为逆时针,面积小于0,则表示a->b->c为顺时针,如果面积等于0,则表示a b c共线


如何高效地排序

使用归并排序,复杂度为NlogN


代码

别看代码这么长,其实核心算法只有11行。


import  java.util.Arrays;
import  java.util.Stack;
 
public  class  ConvexHull {
     public  static  Point2D[] vertex(Point2D[] li) {
         // 复制坐标点
         int  N = li.length;
         Point2D[] li2 =  new  Point2D[N];
         for ( int  i= 0 ;i<N;i++){
             li2[i] = li[i];
         }
 
         // 找出y坐标最小的点
         for ( int  i= 1 ;i<N;i++){
             Point2D e=li[i];
             boolean  swap =  false ;
             if (e.y<li[ 0 ].y){
                 swap= true ;
             else  if (e.y==li[ 0 ].y && e.x > li[ 0 ].y){
                 swap= true ;
             }
 
             if (swap){
                 Point2D temp = li[ 0 ];
                 li[ 0 ] = li[i];
                 li[i] = temp;
             }
         }
         Point2D minPoint = li[ 0 ];
 
         // 计算每个顶点的角度
         AnglePoint[] angles =  new  AnglePoint[N];
         angles[ 0 ] =  new  AnglePoint(li[ 0 ],- 999 );
         for ( int  i= 1 ;i<N;i++){
             Point2D pt = li[i];
             float  angle = getAngle(minPoint, pt);
             angles[i] =  new  AnglePoint(pt, angle);
         }
 
         // 按照角度进行排序
         Arrays.sort(angles);
 
         // 凸包的核心算法
         Stack<Point2D> result =  new  Stack<Point2D>();
         result.push(angles[ 0 ].point);
         result.push(angles[ 1 ].point);
         for ( int  i= 2 ;i<N;i++){
             Point2D top = result.pop();
             Point2D pt = angles[i].point;
             while (!result.isEmpty() && ccw(result.peek(),top,pt) <=  0 ){ //第一个点是result.peek()而不是minPoint。不要忘了isEmpty
                 top = result.pop();
             }
             result.push(top);
             result.push(pt);
         }
 
         // 返回顶点的数组
         return  result.toArray( new  Point2D[ 0 ]);
     }
 
     public  static  float  ccw(Point2D a, Point2D b, Point2D c){
         return  (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);
     }
 
     public  static  float  getAngle(Point2D a, Point2D b){
         float  dx = b.x-a.x;
         float  dy = b.y-a.y;
         double  cos = (dx/Math.sqrt(dx*dx+dy*dy));
         return  ( float )Math.acos(cos);
     }
}
 
class  Point2D {
     public  float  x;
     public  float  y;
 
     public  Point2D( float  x,  float  y){
         this .x=x;
         this .y=y;
     }
}
 
class  AnglePoint  implements  Comparable<AnglePoint> {
     public  Point2D point;
     public  float  angle;
 
     public  AnglePoint(Point2D point,  float  angle) {
         this .point = point;
         this .angle=  angle;
     }
 
     @Override
     public  int  compareTo(AnglePoint that){
         if ( this .angle < that.angle){
             return  - 1 ;
         else  if  ( this .angle > that.angle) {
             return  1 ;
         else  {
             return  0 ;
         }
     }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值