C语言实现凸包Graham_scan算法

算法就不介绍了,其他地方应该也搜得到。

如何判断三点A, B, C连线是逆时针?

(1) 通过判断C在AB连线上方还是下方判断,但需要根据斜率的正负,A, B位置关系分类讨论,比较麻烦。
(2) 通过计算向量叉积的方法。
在一般的常识或者教科书中规定叉乘只有3维才拥有,其实2维也可以拓展出来一个叉乘形式。
拓展方式:假设有两个2维向量a,b,我们直接把他们视为3维向量,z轴补0,那么这个时候的a,b向量的叉乘结果c,c.x=0,c.y=0,c.z=a.x* b.y-b.x *a.y。
有这样的性质:设k是a,b向量的叉积,如果k>0时,那么a正旋转到b的角度为<180°,如果k<0,那么a正旋转到b的角度为>180°,如果k=0 那么a,b向量平行。

代码

#include<stdio.h>
#include<math.h>
#include<time.h>
#include<stdlib.h>
#define PI 3.14159
typedef struct Point {
	int x, y;
	double angle;
}Point;
int less(Point a, Point b) {		//定义点a小于点b法则(排序用)
	if (a.angle != b.angle) return a.angle < b.angle;
	else return a.x < b.x;
}
int ifAnticlockwise(Point a, Point b, Point c) {	//是否为逆时针
	int crossProduct = (b.x - a.x)*(c.y - b.y) - (b.y - a.y)*(c.x - b.x);	//求向量积
	return crossProduct >= 0;	//向量积为正表示逆时针
}
int main() {
	srand((unsigned)time(NULL));
	int n, minx, maxx, miny, maxy;
	scanf("%d%d%d%d%d", &n, &minx, &maxx, &miny, &maxy);
	Point point[105];
	for (int i = 0; i < n; i++) {	//生成随机座标
		point[i].x = rand() % (maxx - minx + 1) + minx;
		point[i].y = rand() % (maxy - miny + 1) + miny;
	}
	double sx = 1000, sy = 1000;
	for (int i = 0; i < n; i++) {		//寻找起始点
		if (point[i].y < sy) sx = point[i].x, sy = point[i].y;
		else if (point[i].y == sy && point[i].x < sx) sx = point[i].x;
	}
	for (int i = 0; i < n; i++) {		//求夹角
		if (point[i].x == sx && point[i].y == sy) point[i].angle = 0;
		else if (point[i].x == sx) point[i].angle = 90;
		else {
			point[i].angle = atan((point[i].y - sy)*1.0 / (point[i].x - sx));
			if (point[i].angle < 0) point[i].angle = PI + point[i].angle;	
//转换为与x轴正方向夹角
		}
	}
	for (int i = 0; i < n - 1; i++) {
		for (int j = i + 1; j < n; j++) {	//冒泡排序
			if (less(point[j], point[i])) {
				Point temp = point[i];
				point[i] = point[j];
				point[j] = temp;
			}
		}
	}
	int cnt = 3;	//栈顶指针
	for (int i = 3; i < n; i++) {

		if (ifAnticlockwise(point[cnt - 2], point[cnt - 1], point[i])) {
			point[cnt++] = point[i];	//模拟入栈
		}
		else point[cnt - 1] = point[i];	//模拟出栈+入栈
	}
	for (int i = cnt - 1; i >= 0; i--) {	//逆序输出
		printf("%4d %4d    ", point[i].x, point[i].y);
	}
	return 0;
}

程序测试

为了验证程序的正确性,先缩小了样本,随机生成横纵座标都在-10~10范围内的6个点,输出第一行为随机生成的6个点的横纵座标,接着输出结果。将点在matlab中绘制,可以验证程序正确。

在这里插入图片描述在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章