算法导论学习笔记(五)计算几何之寻找凸包

定义:点集Q的凸包是一个最小的凸多边形P,满足Q中的每个点都在P的边界上或在P的内部。

Graham扫描法

通过排序所有点,选择一个一定是凸包中的点作为基点,再在剩余点中按相对于基点的极角排序,最后依次遍历所有点,将不符合凸多边形定义的点去除,剩下的点集便是凸包中的点。

伪代码

GRAHAM-SCAN( Q)
    Q.sort( 0, 1, 2..., n-1) with 
    (pi.y==pj.y)? (pi.x<pj.x) : (pi.y<pj.y)
    //以p0点为原点,建立极坐标
    Q.sort( 1, 2..., n-1) with
          (pi.θ==pj.θ)? (pi.λ<pj.λ) : (pi.θ<pj.θ)
    let S be an empty stack
        PUSH( p0, S)
        PUSH( p1, S)
        PUSH( p2, S)
        for i = 3 to n
            while    pi makes a nonleft turn
                POP( S)
            PUSH( pi, S)
    return S

代码实现

#include <cmath>
#include <algorithm>
const double ACCURACY = 0.0000001;
typedef struct Point
{
    int x,y;
}VectorLine;
VectorLine operator- ( Point a, Point b) 
{
    VectorLine c;
    c.x = a.x - b.x;
    c.y = a.y - b.y;
    return c;
}
bool cmpX( Point a, Point b )
{
    if( a.x == b.x )
        return a.y < b.y;
    return a.x < b.x;
}
double getDistance( Point a, Point b )
{
    return sqrt( pow(a.x-b.x,2.0) + pow(a.y-b.y,2.0) );
}
struct Node
{
    Point point;
    double degree;
    double distance;
};
void setNode(Node &a, Point source )
{
    double distance = getDistance( a.point, source );
    double deltaY = (a.point).y - source.y;
    double degree = asin( deltaY/distance );
    a.distance = distance;
    a.degree = degree;
}
bool cmpPoint( Node a, Node b )
{
    return cmpX( a.point, b.point );
}
bool cmpDegree( Node a, Node b )
{
    if( fabs( a.degree-b.degree ) < ACCURACY )
        return a.distance < b.distance;
    return a.degree < b.degree;
}
int getCrossProduct( VectorLine a, VectorLine b )
{
    return a.x*b.y - a.y*b.x;
}
bool check( Point newPoint, Point oldPoint, Point sourcePoint )
{
    VectorLine oldLine = oldPoint - sourcePoint;
    VectorLine newLine = newPoint - sourcePoint;
    return getCrossProduct( oldLine, newLine ) > 0;
}
int getConvexHull( Node *nodes, int sizeOfNodes, Point *convexHull )
{
    //Graham's scan
    std::sort( nodes, nodes+sizeOfNodes, cmpPoint );

    for( int i=1; i<sizeOfNodes; i++ ) {
        setNode(nodes[i],nodes[0].point);
    }
    std::sort( nodes+1, nodes+sizeOfNodes, cmpDegree );

    int sizeOfStack = 0;
    for( int i=0;i<std::min(3,sizeOfNodes);i++ ) {
        convexHull[sizeOfStack++] = nodes[i].point;
    }

    for( int i=3;i<sizeOfNodes;i++ ) {
        while( sizeOfStack > 2 && 
                !check( nodes[i].point,
                        convexHull[sizeOfStack-1],
                        convexHull[sizeOfStack-2] )
        ) {

            sizeOfStack--;
        }
        convexHull[sizeOfStack++] = nodes[i].point;
    }
    return sizeOfStack;
}

Jarvis步进法

将点设为有物理特性,利用一条可弯曲的射线,将一端放在一个最左边的点上,将射线方向设为180度,逆时针旋转,所遇见的第一个为下一个凸包点,然后依次挑选,直至点为第一个点时,所选出的点集为凸包。

伪代码

JARVIS( Q)
    Q.sort( 0, 1, 2..., n-1) with 
    (pi.y==pj.y)? (pi.x<pj.x) : (pi.y<pj.y)
    let nowPoint = 0 and nextPoint = -1
    while nextPoint != 0
        //以p0点为原点,建立极坐标
        nextPoint in Q
        //nextPoint为Q中极角最小的点
        nowPoint = nextPoint
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值