清华大学的邓俊辉老师的<计算几何>公开课中,在计算凸包问题时会遇到极点法和极边法:
- 极点法是假设所有的点都是凸包上的点,然后根据In-triangle测试,把去除不是极点的点,时间复杂度是O(n^4);
- 为了解决时间复杂度太高问题,引入极边法,假设所有的边都不是凸包上的边,然后判断每一条边是否只有一侧有点,那么他就是凸包上的边.这个时间复杂度是O(n^3).
- 为了进一步降低时间复杂度,利用分治法,类似于插入排序.逐个的取点,若是极点,把它加入到集合中,每次加入一个新点,判断引入新的点,对于已经存在的点的前驱点和后继点是否是前驱点在新插入点与当前点的右侧,后继点在右侧,说明这个当前点还是凸包上的点,若是反着的顺序,则,在插入新的点后,当前的点就不是凸包上的点了.这里实际上存在两种情况,若新插入的点,在已经存在的点构成的多边形内部,则所有的点都是RL模式,否则,必然存在LR模式,也存在两个分界点.在具体实现的时候,还会遇到在线上的点,这种确实很头疼.暂时用这种方法很难解决.
前两种方法在前面的一个文章实现了,比较简单,这里实现第三种方法,增量计算凸包,但是不能处理多个点共线问题.
#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;
struct point
{
point(int _x = 0, int _y = 0) : x(_x), y(_y) {}
int x;
int y;
};
enum Loc
{
left,
online,
right
};
struct node
{
node(point _p) : p(_p), next(nullptr), prev(nullptr) {}
point p;
node *prev;
node *next;
};
int Area(point p, point q, point s)
{
return p.x * q.y - p.y * q.x +
q.x * s.y - q.y * s.x +
s.x * p.y - s.y * p.x;
}
Loc ToLeft(point p, point q, point s)
{
auto area = Area(p, q, s);
if (area > 0)
return Loc::left;
if (area == 0)
return Loc::online;
if (area < 0)
return Loc::right;
}
int main()
{
vector<point> points = {
{1, 1},
{2, 2},
{2, 0},
{2, 4},
{3, 3},
{4, 2}
};
node *p = new node(points[0]);
p->prev = new node(points[1]);
p->next = p->prev;
p->next->prev = p;
p->next->next = p;
int resCount = 2;
for (int i = 2; i < points.size(); ++i)
{
node *s = nullptr;
node *t = nullptr;
node *q = p;
for (int j = 0; j < resCount; ++j)
{
node *prevNode = q->prev;
node *nextNode = q->next;
Loc prevLoc = ToLeft(points[i], q->p, prevNode->p);
Loc nextLoc = ToLeft(points[i], q->p, nextNode->p);
if (prevLoc == Loc::left &&
(nextLoc == Loc::left || nextLoc == Loc::online))
{
s = q;
q = q->next;
}
else if ((prevLoc == Loc::right || prevLoc == Loc::online) &&
nextLoc == Loc::right )
{
t = q;
q = q->next;
}
else if (prevLoc == Loc::left && nextLoc == Loc::right)
{
node *tt = q;
q->prev->next = tt->next;
q->next->prev = tt->prev;
q = tt->next;
delete tt;
resCount--;
}
else
q = q->next;
}
if (s != nullptr && t != nullptr)
{
node *tt = new node(points[i]);
tt->next = s;
s->prev = tt;
tt->prev =t;
t->next = tt;
q = tt;
resCount++;
}
p = q;
}
for (int i = 0; i < resCount; ++i)
{
cout << p->p.x << " " << p->p.y << endl;
p = p->next;
}
return 0;
}