测试凸包c语言程序代码,基于C语言的凸包算法实现

基于C语言的凸包算法实现

非计算机专业,代码有些的不好的地方,大佬轻喷^ _ ^

根据要求,需要使用C语言实现凸包算法——Graham扫描法,本文将从算法理解、实现思路、遇到的问题及其解决方案三个方面来阐述实现过程。

算法理解

凸包算法Graham扫描法,在不考虑排序算法的时间复杂度情况下,算法核心程序的时间复杂度为 O ( n l o g n ) O(n log n)O(nlogn),其主要算法思想如下:

首先是预处理过程,获得一组随机点集,选取位于二维空间中左下角的点,即在纵坐标(y)最小情况下横坐标(x)为最小的点P 0 P_0P0​ 。以该点位极坐标原点计算其余各点的极角θ \thetaθ,并根据极角大小进行升序排序,若极角相同则按极径大小按升序排列。由此得到一组按照极角排序的点集 { P 0 , P 1 , . . . , P n } \left\{P_0,P_1,...,P_n\right\}{P0​,P1​,...,Pn​}(如下图所示)。

5977949994e18c22e4f8ebc03f68f0b0.png

完成预处理之后即Graham算法的核心步骤,主要通过栈的方式来实现凸包点的计算。首先将P 0 , P 1 P_0,P_1P0​,P1​两点压栈,他们必然属于凸包上的点。然后进入迭代过程,以栈顶元素A [ t o p ] A[top]A[top]和次栈顶元素 A [ t o p − 1 ] A[top-1]A[top−1]构成的向量a ⃗ \vec{a}a为基准计算其与当前P k P_kPk​点与栈顶元素A [ t o p ] A[top]A[top]构成的向量b ⃗ \vec{b}b的叉积 ,若结果为正(零)则 b ⃗ \vec{b}b 位于 a ⃗ \vec{a}a 的逆时针方向(共线),P k P_kPk​进栈 ,若结果为负则 b ⃗ \vec{b}b 位于 a ⃗ \vec{a}a 的顺时针方向, A [ t o p ] A[top]A[top]出栈, P k P_kPk​进栈,直至扫描至最后一个点,将 P 0 P_0P0​ 再次进栈是凸包闭合。

由于要求顺时针输出凸包顶点,则将栈中元素从栈顶向栈底依次输出即可。

实现思路及过程

根据Graham扫描法的算法理解,将程序实现分为了随机点坐标初始化、极角计算及排序、Graham核心算法和结果输出四个模块共计8个函数进行编码实现。

结构体定义

// 点坐标

typedef struct POINT {

int x;

int y;

}Point;

坐标初始化

首先构造存储点坐标的结构体Point,该结构体中仅包含横坐标x和纵坐标y。根据要求需要随机生成100个点,使用宏定义点集大小(SIZE)为100。使用库下的rand()函数以当前系统时间为种子生成 0 ≤ x < 50 , 0 ≤ y < 50 0\le x<50,0\le y<500≤x<50,0≤y<50 的点,并依次存入大小为SIZE的Point的类型的一维数组中。

void InitPoint(Point* p) {

int i;

srand(time(0));

for (i = 0; i < SIZE; i++) {

(p + i)->x = (int)(rand() % 50);

(p + i)->y = (int)(rand() % 50);

}

}

极角计算及排序

首先选取点集中位于左下角的点,采用的方法为先找出纵坐标 最小的坐标点(集),然后在其中找出横坐标 最小的坐标点,记录该点位于原始点集的位置,将其与第一个点进行交换。接着使用库下的atan()函数计算各点的极角,并将其记录在double类型大小为SIZE的一维数组angle中,令极坐标原点的极角: a n g l e [ 0 ] = 0 angle[0] = 0angle[0]=0。使用冒泡排序算法对极角进行排序,同时改变点集中各点的顺序。在排序是要考虑当极角相同时按极径从小到大排序。

Graham核心算法

根据算法分析结果,定义一个Point类型的一维数组作为栈空间,定义栈顶定位变量top,用于标记栈顶元素在栈中的位置,定义临时变量temp_point用记录当前扫描到的坐标点,定义叉积计算函数,返回值为布尔类型。当叉积为非负时返回true,否则返回false。判断temp_point和栈顶元素,次顶元素三个点组成的两个向量的方向,若叉积返回值为正,则将temp_point进栈,否则将当前栈顶元素出栈,继续判断现在的栈顶元素和次顶元素与temp_point三个点的向量叉积……

得到包含所有凸包顶点的栈数组,最后将 点进栈形成封闭凸包,在形成封闭凸包前需要对 以及当前栈顶和次顶元素进行判断是否符合凸包结构,若符合则将 进栈,反之将当前栈顶元素出栈,重复判断步骤直至符合为止。

int myGraham(Point* p, Point* p_stack) {

int top = -1; //栈顶指针

int p_index = 0; //点索引

Point temp_point;

top++; p_stack[top] = p[0]; p_index++; //push

top++; p_stack[top] = p[1]; p_index++; //push

while (p_index < SIZE) {

temp_point = p[p_index];

if (X(p_stack[top - 1], p_stack[top], temp_point))

{

top++; p_stack[top] = temp_point;//push

}

else {

top--;//pop

continue;

}

p_index++;

}

while (TRUE) {

if (!X(p_stack[top - 1], p_stack[top], p[0])) {

top--;

}

else {

break;

}

}

top++; p_stack[top] = p[0];

return top;

}

结果输出

为了使结果更直观,定义了一个输出函数,能够输出凸包顶点坐标,并在二维坐标中,显示点。

运行结果

e30445ef37352a4cfef119e76a59385a.png

a489290214b61194bdaafbef9dd3faba.png

07dfc6157fe9dca4c025d1f6c7e719b1.png

来源:oschina

链接:https://my.oschina.net/u/4355012/blog/4274913

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值