凸包问题(2)------graham扫描法

本文深入探讨了在计算机图形学中如何使用Graham扫描算法有效地找到给定点集的凸包。通过详细步骤解析,读者将理解该算法的工作原理及其在实际问题中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#include<iostream>
#include<vector>
#include<math.h>
#include<algorithm>
using namespace std;
 
vector<vector<double>> findNodes(vector<vector<double>>& arr);
bool sortByAngle(vector<double>& a, vector<double>& b);
bool isOrigin(vector<double>& a, vector<double>& b);
bool isOnLeft(vector<double> curNode, vector<double> topMinuOne, vector<double> top);
double distance(vector<double> &a, vector<double> &b);
void test1();
void test2();
 
vector<double> originPoint;
double originX;
double originY;
 
int main() {
	test1();   //当坐标数组为空时候,测试1
	test2();  //当扫描时候有多个坐标在一条直线上时,测试2
}
 
void test1() {
	cout << "test1()输入为空数组:" << endl;
	vector<vector<double>> temp;
	if (findNodes(temp).size() == 0) {
		cout << "test1()输出为空数组,正确"     <<endl<<endl << "-------------" << endl;
	}
	else {
		cout << "test1()输出不为空数组,错误:" <<endl<<endl << "--------------" << endl;
	}
}
 
void test2() {
	vector<vector<double>> temp = { { 1,1 },{ 2,2 },{ 2,0 },{ 2,4 },{ 3,3 },{ 4,2 } };
	//vector<vector<double>>     temp = { {1,1},{2,1},{3,1},{2,2},{3,3},{2,3},{1,3} };
	//vector<vector<double>>     temp = { {1,1},{1,1},{1,1},{1,1},{1,1},{1,1} };
	cout << "test2()输入是:     " << "{{1,1},{2,2},{2,0},{2,4},{3,3},{4,2}}" << endl;
	vector<vector<double>> ans = findNodes(temp);
	cout << "test2()输出结果是:{";
	for (int i = 0; i < ans.size(); i++) {
		cout << '{' << ans[i][0] << "," << ans[i][1] << "},";
	}
	cout << '}' << endl;
	cout << "test2()正确结果是:" << "{{2,0},{4,2},{2,4},{1,1}}";
}
 
//Description: 找出凸包上的坐标点,会先找到一个极点然后进行逆时针扫描
vector<vector<double>> findNodes(vector<vector<double>>& arr) {
 
	sort(arr.begin(), arr.end(), isOrigin); //排序目的有两个,一是用于查找第一个节点(左下角)
	int nodeNum = arr.size();				//二是为了给坐标点去重
	for (int i = 0; i < nodeNum - 1; i++) {
		if ((arr[i][0] == arr[i + 1][0]) && (arr[i][1] == arr[i + 1][1])) {
			arr.erase(arr.begin() + i);
			nodeNum = arr.size();
			i--;
		}
	}
 
	nodeNum = arr.size(); //计算去重后剩下的坐标点的个数
	if (nodeNum <= 2) { //数组为空或者坐标点为1,2这些简单情况就直接返回
		return arr;
	}
 
	originPoint = arr[0];
	originX = originPoint[0];
	originY = originPoint[1];
 
	sort(arr.begin() + 1, arr.end(), sortByAngle); //根据角度大小进行排序,角度一样大的按照距离小的在前
 
	vector<vector<double>> ans;
	ans.push_back(originPoint);	//把极点先存入
 
	int i = 1;
	ans.push_back(arr[i++]);
	int top = 1;
	while (i < nodeNum) {  //检查每一个节点是否属于凸包
		if (isOnLeft(arr[i], ans[top - 1], ans[top])) {
			ans.push_back(arr[i++]);
			top++;
		}
		else {
			if (ans.size() == 2) { //如果刚开始的几个点都在一条直线上,那么ans里面第二个点会被剔除,导致数组越界
				ans.pop_back();
				ans.push_back(arr[i++]);
			} else {
				ans.pop_back();
				top--;
			}
		}
	}
	return ans;
}
 
//Description: 判断三点构成的连线的相对位置
bool isOnLeft(vector<double> cur, vector<double> topMinuOne, vector<double> top) {
	int cross = (cur[0] - topMinuOne[0])*(top[1] - topMinuOne[1]) - (top[0] - topMinuOne[0])*(cur[1] - topMinuOne[1]); //x1y2-x2y1 计算叉积
	if (cross > 0) {
		return false;
	}
	else if (cross < 0) {
		return true;
	}
	else { //叉积等于0的时候,说明点共线,要进行判断一下
		if (distance(cur, topMinuOne) > distance(top, topMinuOne)) {
			return false;
		}
		return true;
	}
}
 
bool isOrigin(vector<double>& a, vector<double>& b) {
	if (a[1] == b[1]) { //纵坐标相等的时候,横坐标小的在前
		return a[0] < b[0];
	}
	return a[1] < b[1];
}
 
double distance(vector<double> &a, vector<double> &b) {
	return (pow((a[0] - b[0]), 2) + pow((a[1] - b[1]), 2));
}
 
bool sortByAngle(vector<double>& a, vector<double>& b) {
	if (abs(atan2(a[1] - originY, a[0] - originX) - atan2(b[1] - originY, b[0] - originX)) < 1e-8) { //浮点数比较是否相等,只能取一个范围
		return distance(a, originPoint) < distance(b, originPoint);
	}
	return atan2(a[1] - originY, a[0] - originX) < atan2(b[1] - originY, b[0] - originX);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值