凸包算法

平面凸包 :

定义: 对一个简单多边形来说,如果给定其边界上或内部的任意两个点,连接这两个点的线段上的所有点都被包含在该多边形的边界上或内部的话,则该多边形为凸多边形 。

求覆盖平面上n 个点的最小的凸多边形。也可以这样描述:给定一个连接的多边形,可能是凸多边形,也有可能是凹多边形。现在,你的任务就是编程求这个多边形的最小凸包。如果它本身是凸多边形,那么最小凸包就是它本身。

凸包算法_第1张图片

Graham扫描法

基本思想:通过设置一个关于候选点的堆栈s来解决凸包问题。

操作:输入集合Q中的每一个点都被压入栈一次,非CH(Q)(表示Q的凸包)中的顶点的点最终将被弹出堆栈,当算法终止时,堆栈S中仅包含CH(Q)中的顶点,其顺序为个各顶点在边界上出现的逆时针方向排列的顺序。

注:下列过程要求|Q|>=3,它调用函数TOP(S)返回处于堆栈S 顶部的点,并调用函数NEXT-TO –TOP(S)返回处于堆栈顶部下面的那个点。但不改变堆栈的结构。

GRAHAM-SCAN(Q)
1 设P0 是Q 中Y 坐标最小的点,如果有多个这样的点则取最左边的点作为P0;
2 设< P1,P2,……,Pm >是Q 中剩余的点,对其按逆时针方向相对P0 的极角进行排序,如果有数个点有相同的极角,则去掉其余的点,只留下一个与P0 距离最远的那个点;
3 PUSH(p0 , S)
4 PUSH(p1 , S)
5 PUSH(p3 , S)
6 for i ← 3 to m
7 do while 由点NEXT-TOP-TOP(S),TOP(S)和Pi 所形成的角形成一次非左转
8 do POP(S)
9 PUSH(pi , S)
10 return S

首先,找一个凸包上的点,把这个点放到第一个点的位置P0。然后把P1~Pm 按照P0Pi的方向排序,可以用矢量积(叉积)判定。
做好了预处理后开始对堆栈中的点< p3,p4,…,pm>中的每一个点进行迭代,在第7到8行的while循环把发现不是凸包中的顶点的点从堆栈中移去。(原理:沿逆时针方向通过凸包时,在每个顶点处应该向左转。因此,while循环每次发现在一个顶点处没有向左转时,就把该顶点从堆栈中弹出。)当算法向点pi推进、在已经弹出所有非左转的顶点后,就把pi压入堆栈中。
举例如下:

凸包算法_第2张图片
凸包算法_第3张图片
凸包算法_第4张图片
凸包算法_第5张图片
凸包算法_第6张图片
凸包算法_第7张图片

代码:
//Graham.h

#ifndef __GRAHAM_H
#define __GRAHAM_H
#include 
#include 
#include 

using namespace std;
struct Point
{
    double x;
    double y;
};
typedef struct Point Point;
double cross_product(const Point p0,const Point& p1,const Point& p2);
double distan(const Point& p0,const Point& p1,const Point& p2);
bool comp(const Point& p1,const Point& p2);
void findTubao(vector& povec);
void inputPointSet();

#endif

//Graham.cc

#include "Graham.h"
#include 

Point pp0;

double cross_product(const Point p0, const Point& p1, const Point& p2)
{
    return (p1.x - p0.x)*(p2.y - p0.y) - (p2.x - p0.x)*(p1.y - p0.y);
}
double distan(const Point& p0,const Point& p1)
{
    return (p1.x - p0.x)*(p1.x - p0.x) + (p1.y - p0.y)*(p1.y - p0.y);
}
bool comp(const Point& p1,const  Point& p2)
{
    double temp = cross_product(pp0, p1, p2);
    if (temp < 1e-6)
    {
        return distan(pp0, p1)else
    {
        return temp>0;
    }
}

void findTubao(vector& povec)
{
    //找出Y坐标最小点

    int siz = povec.size();
    if (siz == 0)
        return;

    int index = 0;
    double min = povec[0].y;
    for (int i = 1; i < siz; ++i)
    {
        if (povec[i].y < min)
        {
            min = povec[i].y;
            index = i;
        }
    }
    swap(povec[0], povec[index]);
    pp0 = povec[0];

    sort(povec.begin() + 1, povec.end(), comp);
    vector res;
    res.push_back(povec[0]);
    res.push_back(povec[1]);
    res.push_back(povec[2]);
    int top = 2;
    for (int i = 3; i < siz; ++i)
    {
        while (top>0 && cross_product(res[top - 1], povec[i], res[top]) > 0)
        {
            --top;
            res.pop_back();
        }
        ++top;
        res.push_back(povec[i]);
    }

    //打印点
    for (int i = 0; i < res.size(); ++i)
    {
        cout << "(" << res[i].x << "," << res[i].y << ")" << endl;
    }

}
void inputPointSet()
{
    Point po;
    int x;
    vector inp;
    cin >> x;
    while (x > 0)
    {
        cin >> po.x >> po.y;
        inp.push_back(po);
        --x;
    }
    findTubao(inp);
}

写代码时候遇到的bug:
1、不要用distance。。与stl中的一个东西重名了
2、比较两个double值请于1e-6啥的比较,并且是fabs(x1-x2)<1e-6来比较,而不是(x1-x2)<1e-6来比较是否相等,fabs是取绝对值的意思

你可能感兴趣的:(C/C++,算法)