课程:计算几何
书籍:计算几何:算法与应用
具体思路请自行观看上述的课程。
这里具体讲一下顶点集的排序:
我们通过X轴从大到小排序,然后运用Graham Scan算法可以得出构成上凸包的顶点。
然后逆序顶点顺序,运用Graham Scan算法可以得出构成下凸包的顶点。
结合两者,去掉重复点,即可得到构成整个凸包的顶点。
Point.h与之前一样
Main.cpp
/*2018/07/03 indere Convex Hull
* 时间复杂度为Nlog(N)的算法思路: Graham Scan
*
*输入:多个顶点。
*输出:构成凸包的极点
*/
/*
10
7 9
-8 -1
-3 -1
1 4
-3 9
6 -4
7 5
6 6
-6 10
0 8
*/
#include "Point.h"
#include<vector>
using namespace std;
Point *points;
int pointsize;
Point *sortpoints;
void Initialize(); //初始化points数组
void UpSort(Point* points,int beign, int pointsize); //根据X坐标从大到小排序
int QSortByX(Point* points, int begin, int end); //快速排序
void Swap(Point* p, int i, int j); //交换points中的两个元素
vector<Point> Create_Up_Down_CH(Point* points, int pointsize); //构造上/下凸包
int ToLeft(Point v1, Point v2, Point s); //
void Reverse(Point* points, int pointsize); //反转顶点顺序
void PrintVector(vector<Point> s, int begin, int end);
int main() {
Initialize();
int begin = 0;
UpSort(points, begin, pointsize);
vector<Point> up = Create_Up_Down_CH(points, pointsize);
cout << "上凸包为:\n";
PrintVector(up, 0, up.size());
Reverse(points, pointsize);
vector<Point> down = Create_Up_Down_CH(points, pointsize);
cout << "下凸包为:\n";
PrintVector(down, 0, down.size());
cout << "凸包的极点为:\n";
PrintVector(up, 0, up.size());
PrintVector(down, 1, down.size() - 1);
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);
}
}
void UpSort(Point* points, int begin, int pointsize) {
if (begin >= pointsize)
return;
int m = QSortByX(points, begin, pointsize);
UpSort(points, begin, m);
UpSort(points, m + 1, pointsize);
}
int QSortByX(Point* points, int begin, int end) {
int i, j, temp;
i = begin - 1; j = begin;
for (; j < end; j++) {
if (points[j].x >= points[end - 1].x) {
Swap(points, ++i, j);
}
}
return i;
}
void Swap(Point* p, int i, int j) {
Point temp;
temp = p[i];
p[i] = p[j];
p[j] = temp;
}
vector<Point> Create_Up_Down_CH(Point* points, int pointsize) {
vector<Point>s;
vector<Point>t;
if (pointsize < 3) {
cout << "并不能构成凸包\n";
return s;
}
for (int i = 0; i < 2; i++)
s.push_back(points[i]);
for (int i = pointsize - 1; i >= 2; i--)
t.push_back(points[i]);
while (t.size()) {
if (s.size() == 1) {
s.push_back(t.back());
t.pop_back();
continue;
}
int result = ToLeft(s[s.size() - 2], s[s.size() - 1], t.back());
if (result == 1) {
s.push_back(t.back());
t.pop_back();
}
else {
s.pop_back();
}
}
return s;
}
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;
}
void Reverse(Point* points, int pointsize) {
Point temp;
for (int i = 0; i < pointsize / 2; i++) {
temp = points[i];
points[i] = points[pointsize - 1 - i];
points[pointsize - 1 - i] = temp;
}
}
void PrintVector(vector<Point> s, int begin, int end) {
for (; begin < end; begin++)
cout << s[begin];
}