关闭

凸包问题

标签: java分治算法递归凸包
1197人阅读 评论(0) 收藏 举报
分类:

一、凸包的概念

1.1、点集Q的凸包(convex hull)是一个最小的凸多边形P,满足Q中的每个点,或则在P的边界上或则在P的内部。下图中由红色线段表示的多边形就是点集Q={p0,p1,...p12}的凸包。


1.2、解决的算法:分治法、增量式算法、包裹法(Jarvis步进法)、葛立恒(Graham)扫描法、快包法(Akl-Toussaint启发式)等。

二、分治法解决策略

2.1、分治法:所谓分治法就是把问题划分成多个子问题来进行处理。这些子问题,在结构上与原来的问题一样,但在规模上比原来的小。如果得到的子问题相对来说还大,可以反复地使用分治策略,把这些子问题再划分成更小的、结构相同的子问题。这样就可以使用递归的方法,分别求解这些子问题,把这些子问题的解结合起来,从而获得原来问题的解。

分治法的求解过程:
(1)划分:把规模为n 的原问题划分为k 个规模较小且规模大致相同的子问题。
(2)求解子问题:使用递归方法分别求解子问题。
(3)合并:把各子问题的解合并起来,合并的代价因情况而异,分治算法的有效性很大程度上依赖于合并的实现。

2.2、凸包问题的分治思想
第一步:把给定点集中的点在横坐标方向上按照大小排序。如下图所示,p1 和pn 必定是凸多边形的两个顶点。


第二步:在上凸包点集合s1 中找到一个距离直线最远点pmax,如下 图所示。显然直线段p1pmax与直线段pmaxpn把点集s1分成了三个集合。由凸包的性质可知p1,pmax,pn三点围成的三角形中的点不可能作为凸包的顶点,所以只需考虑直线p1pmax左边的点s11以及直线pmaxpn右边的点s12。


第三步:递归求解得到凸多边形的边。
第四步:合并这些边即得所求凸包。

2.3、凸包问题的分治代码(java)

import java.util.*;
class Line{//线   
    Point p1,p2;
    Line(Point p1,Point p2){this.p1=p1;this.p2=p2;}
    public Point getP1(){return p1;}
    public Point getP2(){return p2;}
}
class Point{//点  
    int x;int y;
    public Point(int x,int y){this.x=x;this.y=y;} 
}
/*
*   分治法求凸包
*/
class QuickTuBao{
    List<Point> pts=null;//给出的点集
    List<Line> lines= new ArrayList<Line>();//点集pts的凸包
public void setPointList(List<Point> pts){this.pts=pts;}
public QuickTuBao(List<Point> pts){this.pts=pts;}

//求凸包,结果存入lines中
public List<Line> eval(){
    lines.clear();
    if(pts==null||pts.isEmpty()){return lines;}
    List<Point> ptsLeft = new ArrayList<Point>();//左凸包中的点
              List<Point> ptsRight = new ArrayList<Point>();//右凸包中的点
    //按x坐标对pts排序
    Collections.sort(pts,new Comparator<Point>(){
        public int compare(Point p1,Point p2){
            f(p1.x-p2.x>0)return 1;
            if(p1.x-p2.x<0)return -1;
            return 0;
        }
    });
Point p1=pts.get(0);//最左边的点
    Point p2=pts.get(pts.size()-1);//最右边的点,用直线p1p2将原凸包分成两个小凸包
    Point p3=null;
    double area=0;
    for(int i=1;i<pts.size();i++){//穷举所有的点,
        p3=pts.get(i);
        area = getArea(p1,p2,p3);//求此三点所成三角形的有向面积
        if(area>0){
            ptsLeft.add(p3);//p3属于左
        }else if(area<0){
            ptsRight.add(p3);//p3属于右
        }
    }
    //分治求解
    d(p1,p2,ptsLeft);
    d(p2,p1,ptsRight);
    return lines;
}
private void d(Point p1,Point p2,List<Point> s){
    //s集合为空
    if(s.isEmpty()){
        lines.add(new Line(p1,p2));
        return;
    }
    //s集合不为空,寻找Pmax
    double area=0;
    double maxArea=0;
    Point pMax=null;
    for(int i=0;i<s.size();i++){
        area=getArea(p1,p2,s.get(i));//最大面积对应的点就是Pmax
        if(area>maxArea){
            pMax=s.get(i);
            maxArea=area;
        }
    }
    //找出位于(p1, pMax)直线左边的点集s1
        //找出位于(pMax, p2)直线左边的点集s2

List<Point> s1=new ArrayList<Point>();
    List<Point> s2=new ArrayList<Point>();
    Point p3=null;
    for(int i=0;i<s.size();i++){
        p3=s.get(i);
        if(getArea(p1,pMax,p3)>0){s1.add(p3);}
        else if(getArea(pMax,p2,p3)>0){s2.add(p3);}
    }
    //递归
    d(p1,pMax,s1);
    d(pMax,p2,s2);
}
// 当且仅当点p3位于直线(p1, p2)左侧时,表达式的符号为正
private double getArea(Point p1,Point p2,Point p3){
    // 三角形的面积等于返回值绝对值的二分之一
    return p1.x*p2.y+p2.x*p3.y+p3.x*p1.y-p1.y*p2.x-p2.y*p3.x-p3.y*p1.x;
}
3.1、列举直角坐标系中13个点的简单示例:

p0(-5,-4),p1(6,-3),p2(4,-2),p3(8,2),p4(6,0),p5(4,2),p6(1,4),
p7(1,1),p8(-3,-2),p9(-5,1),p10(0,6),p11(-2,3),p12(-7,1)


此示例的凸包结果为:
p12(-7,1) --> p10(0,6) --> p3(8,2) --> p1(6,-3) --> p0(-5,-4) --> p12(-7,1)

时间复杂度分析:凸包问题的分治算法,关键步骤是点集按横坐标排序,算法的复杂性是Θ(n㏒n),故凸包问题的分治法的算法复杂性是Θ(n㏒n)。





1
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:72348次
    • 积分:1173
    • 等级:
    • 排名:千里之外
    • 原创:41篇
    • 转载:1篇
    • 译文:1篇
    • 评论:20条
    最新评论