凸包问题

(1)问题描述

用分分治法编写一个求解凸包问题的算法,并测试算法的正确性。
【注:凸包问题】给定平面上n个点,从中找出一个最小点集,使该点集所组成的凸多边形包围所有的n个点。

(2)问题分析

将n个点分为两部分,则每一部分可以形成一个凸包,重复分下去最后将多个凸包合并即可得到n个点的凸包。

(3)算法设计

将n个点按横坐标从小到大排列,则x1,xn必定属于最小点集,x1,xn连线将n个点分成两部分,下面只需找各自的凸包即可。以找上凸包为例,在上半部分的点中找到距离x1,xn连线最远的点,将该点与x1,xn相连则可将上半部分分为3块,则找到上凸包的问题又转化为找出另外两个凸包的问题。上凸包和下凸包要分开处理。递归结束的条件是连线的上方(上凸包)/下方(下凸包)没有点。重复此操作即可得到最后整个的凸包。

(4)算法实现

#include<iostream>
#include<vector>
#include<algorithm>
#include<cmath>
#include<fstream>
#include<sstream>

using namespace std;

struct Point {
	double x;
	double y;
};
inline bool Compx(const Point &p1, const Point &p2)
{
	return p1.x < p2.x;
}
/*
函数:求点p在以p1和p2决定直线的上侧还是下侧
返回值:上侧:>0    下侧:<0    在直线上:=0
*/
inline double Line(const Point &p1, const Point &p2, const Point &p)
{
	return (p1.y - p2.y) * p.x + (p2.x - p1.x) * p.y + (p1.x * p2.y - p1.y * p2.x);
}
/*
函数:求点p到以p1和p2决定直线的距离
*/
double Dist(const Point &p1, const Point &p2, const Point &p)
{
	double A = p1.y - p2.y;
	double B = p2.x - p1.x;
	double C = p1.x * p2.y - p1.y * p2.x;
	return abs(A*p.x + B * p.y + C) / sqrt(A*A + B * B);
}
/*
函数:求解直线p1p2上点集的上包
参数:v:直线p1p2上方的点集 vo:上包点集
*/
void UpHull(const vector<Point> &v, vector<Point> &vo, const Point &p1, const Point &p2)
{
	if (v.size() == 0)
		return;
	if (v.size() == 1) {
		vo.push_back(v[0]);
		return;
	}

	double d = 0;
	int k;
	for (size_t i = 0; i < v.size(); ++i) {
		double t = Dist(p1, p2, v[i]);
		if (t > d) {
			d = t;
			k = i;
		}
	}
	vo.push_back(v[k]);
	vector<Point> vl;
	vector<Point> vr;
	for (size_t i = 0; i < v.size(); ++i) {
		if (Line(p1, v[k], v[i]) > 0)
			vl.push_back(v[i]);
		else if (Line(v[k], p2, v[i]) > 0)
			vr.push_back(v[i]);
	}

	UpHull(vl, vo, p1, v[k]);
	UpHull(vr, vo, v[k], p2);
}
/*
函数:求解直线p1p2下点集的下包
参数:v:直线p1p2下的点集 vo:下包点集
*/
void DownHull(const vector<Point> &v, vector<Point> &vo, const Point &p1, const Point &p2)
{
	if (v.size() == 0)
		return;
	if (v.size() == 1) {
		vo.push_back(v[0]);
		return;
	}

	double d = 0;
	int k;
	for (size_t i = 0; i < v.size(); ++i) {
		double t = Dist(p1, p2, v[i]);
		if (t > d) {
			d = t;
			k = i;
		}
	}
	vo.push_back(v[k]);
	vector<Point> vl;
	vector<Point> vr;
	for (size_t i = 0; i < v.size(); ++i) {
		if (Line(p1, v[k], v[i]) < 0)
			vl.push_back(v[i]);
		else if (Line(v[k], p2, v[i]) < 0)
			vr.push_back(v[i]);
	}

	DownHull(vl, vo, p1, v[k]);
	DownHull(vr, vo, v[k], p2);
}
/*
函数:求解点集v的凸包
*/
void ConvexHull(vector<Point> &v, vector<Point> &vo)
{
	sort(v.begin(), v.end(), Compx);
	vo.push_back(v[0]);
	vo.push_back(v[v.size() - 1]);
	vector<Point> vu;
	vector<Point> vd;
	for (size_t i = 1; i < v.size() - 1; ++i) {
		if (Line(v[0], v[v.size() - 1], v[i]) >= 0)
			vu.push_back(v[i]);
		else if (Line(v[0], v[v.size() - 1], v[i]) < 0)
			vd.push_back(v[i]);
	}
	UpHull(vu, vo, v[0], v[v.size() - 1]);
	DownHull(vd, vo, v[0], v[v.size() - 1]);
}
int main()
{
	vector<Point> v;
	ifstream input("Points.txt", ifstream::in);
	string line;
	Point p;
	while (getline(input, line)) {
		stringstream liness(line);
		liness >> p.x >> p.y;
		v.push_back(p);
	}
	vector<Point> vo;
	ConvexHull(v, vo);
	for (auto p : vo)
		cout << "<" << p.x << "," << p.y << ">" << endl;
	system("pause");
	return 0; 
}

输入文件Points.txt:

0 0
1 5
2 2
3 -1
4 5
5 -3
6 0

(5)运行结果

原文地址:https://www.cnblogs.com/Frank-Hong/p/13335562.html