课程:计算几何
书籍:计算几何:算法与应用
采用一种增量式(Incremental)的思维。考虑在已有的凸包上,在添加一个新的顶点,应该如何操作。
我们可以从新增的点向凸包引射线,我们能找到新增点与凸包的两个切点:s和t,s和t将凸包分成两段。其中有一段上的凸包顶点是需要更新的,另一段上的凸包顶点是需要保留的。具体的怎么判断请大家自行观看上述计算几何课程
代码:
Point.h: 有所更改,为了逆时针记录,类中添加了前后索引。
#pragma once
#ifndef POINT_H
#define POINT_H
#include<iostream>
using namespace std;
class Point {
public:
int index;
float x, y;
bool isExtremePoint;
int pre, next;
public:
Point() { index = 0; x = y = 0.0f; isExtremePoint = false; }
~Point() {}
Point(int index, float xx, float yy) {
this->index = index;
x = xx; y = yy;
}
Point(Point &temp) :index(temp.index), x(temp.x), y(temp.y), isExtremePoint(temp.isExtremePoint){}
friend ostream& operator<<(ostream& os, Point& temp) {
os << "Point index: " << temp.index << " (x,y): " << temp.x << "," << temp.y << endl;
return os;
}
};
#endif // !POINT_H
Mian.cpp
/* indere 2018/6/23
* O(n^2)构造凸包算法是递增式的算法,在已有凸包的基础上,如果需要加入一个新点,应该如何进行删除和增添
* 输入:顶点数以及顶点坐标
* 输出:极点编号(看情况是否按Counter-Clockwise输出)
/
/*
10
7 9
-8 -1
-3 -1
1 4
-3 9
6 -4
7 5
6 6
-6 10
0 8
*/
#include"Point.h"
Point *points;
int pointsize;
Point* CH;
int chsize;
void Initialize(); //初始化points数组
void CCW_sort(Point* points); //将顶点数组进行逆时针排序
int ToLeft(Point v1, Point v2, Point s); //ToLeft Test
int InitialCH(); //初始化最初的Convex Hull:逆时针 包含三个不共线的顶点
void ConstructConvexHull(Point* points, Point* CH, int begin); //构造凸包
int main() {
Initialize();
CCW_sort(points);
int begin = InitialCH() + 1;
if (begin < 0) //不存在新增点,原顶点组不能组成凸包
return -1;
ConstructConvexHull(points, CH, begin);
cout << "The result :" << endl;
for (int i = 0; i < chsize; i++) {
cout << CH[i];
}
return 0;
}
void Initialize() {
cin >> pointsize;
points = new Point[pointsize];
for (int i = 0; i < pointsize; i++) {
float x, y;
cin >> x >> y;
points[i] = Point(i + 1, x, y);
}
//找出Y轴最小的点,放在数组的第一位。方便后续的逆时针排序
int k = 0;
for (int i = 1; i < pointsize; i++) {
if (points[k].y > points[i].y)
k = i;
}
Point temp = points[0];
points[0] = points[k];
points[k] = temp;
}
void CCW_sort(Point* points) {
//我们采用选择排序
int k,begin = 0;
for (int i = 1; i < pointsize; i++) {
k = i;
for (int j = i + 1; j < pointsize; j++) {
int judge = ToLeft(points[begin], points[k], points[j]); //比较器为ToLeft
if (judge != 1) { //不在左侧
if (judge == -1) { //在右侧,更新
k = j;
}
else if (judge == 2) { //共线,如果距离更近,更新
float len1 = (points[j].x - points[begin].x) * (points[j].x - points[begin].x) + (points[j].y - points[begin].y) *(points[j].y - points[begin].y);
float len2 = (points[i].x - points[begin].x) * (points[i].x - points[begin].x) + (points[i].y - points[begin].y) *(points[i].y - points[begin].y);
if (len1 < len2)
k = j;
}
}
}
//swap
Point temp = points[k];
points[k] = points[i];
points[i] = temp;
}
}
int ToLeft(Point v1, Point v2, Point s) {
float result = (v2.x *s.y + v1.x * v2.y + v1.y * s.x)
- (v1.y * v2.x + v1.x * s.y + v2.y * s.x);
if (result > 0.0f) //左侧
return 1;
if (result == 0.0f) //共线
return 2;
if (result < 0.0f) //右侧
return -1;
}
int InitialCH() { //将逆时针排序后的Points中先取三个不共线的点,来构造最初的Convex Hull
CH = new Point[pointsize];
CH[0] = points[0];
int begin = 1, next = 2;
while (ToLeft(points[0], points[begin], points[next]) == 2 && next < pointsize) { //共线,继续寻找
begin++;
next++;
}
if (next >= pointsize) {
cout << "所有顶点均共线,无法组成凸包" << endl;
return -2;
}
CH[1] = points[begin];
CH[2] = points[next];
chsize = 3;
//设置前驱与后继
CH[0].pre = 2;
CH[0].next = 1;
CH[1].pre = 0;
CH[1].next = 2;
CH[2].pre = 1;
CH[2].next = 0;
return next;
}
void ConstructConvexHull(Point* points, Point* CH, int begin) {
int t, s;
bool add_to_ch;
for (int i = begin; i < pointsize; i++) {
add_to_ch = false;
for (int j = 0; j < chsize; j++) {
int judge1 = ToLeft(points[i], CH[j], CH[CH[j].pre]);
int judge2 = ToLeft(points[i], CH[j], CH[CH[j].next]);
if (judge1 == 1 && judge2 == -1) // L + R
CH[j].isExtremePoint = false; //原极点被更新为非极点
else {
CH[j].isExtremePoint = true; //原极点仍然是极点
add_to_ch = true;
if (judge1 != 1 && judge2 != 1) //R + R
t = j;
else if (judge1 != -1 && judge2 != -1) //L + L
s = j;
}
}
if (add_to_ch) {
//增加新点
for (int i = chsize + 1; i > t + 1; i--) {
CH[i] = CH[i - 1];
}
CH[t + 1] = points[i];
CH[t + 1].isExtremePoint = true;
CH[t + 1].pre = t;
CH[t + 1].next = s;
chsize++;
//删除CH中非极点的点
for (int i = 0; i < chsize; i++) {
if (!CH[i].isExtremePoint) {
CH[CH[i].pre].next = CH[i].next;
CH[CH[i].next].pre = CH[i].pre;
for (int j = i + 1; j < chsize; j++) {
CH[j - 1] = CH[j];
}
chsize--;
}
}
}
}
}