凸壳问题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
1
凸壳问题
凸壳问题的格雷厄姆(Graham )扫描法
一、凸壳问题
1、凸壳的定义
定义:令S 是平面上的一个点集,封闭S 中所有顶点的最小凸多边形,称为S 的凸壳,表示为)(S CH 。
)(S CH 上的顶点,有时也叫做S 的极点。
2、凸壳问题 给定平面上n 个点的集合S ,求S 的凸壳)(S CH 。
二、思想方法
1、在平面点集S 中,寻找Y 坐标最小的点。
把它称为0p 。
2、以0p 为源点,对所有点的坐标进行变换。
3、对变换后的所有点,以0p 为源点,计算它们的极坐标幅角。
4、以幅角的非降顺序来排序}{0p S -中的点,令排序过的点为},,,{121-=n p p p T Λ,其中,1p 和1-n p 分别与0p 构成最小与最大的幅
角。
5、把点集T 中的元素,作为事件调度点进行扫描。
6、用堆栈CS 作为扫描过程中局部构成的半封闭凸多边形,把它
2
作为扫描线状态维护。
7、堆栈初始化为},{01p p CS n -=,其中,0p 为栈顶元素。
8、按极坐标的幅角,从1p 开始,到1-n p 为止进行扫描。
9、扫描处理:
假定,在某一个时刻,堆栈内容为:
},,,,,{01k j i n p p p p p CS Λ-=
其中,k p 为栈顶元素,则栈中元素按顺序构成半封闭的凸多
边形。
令l p 是正在扫描的点,如果由j p 、k p 、l p 所构成的路径是左转
的,则由j p 、k p 、l p 形成的边是凸边,把l k p p 作为凸多边形中的
边加入进来,把l p 压入栈顶,把扫描线移到下一点;
如果由j p 、k p 、l p 所构成的路径是右转的,则由j p 、k p 、l p 形成的边是凹边,k p 不是凸壳的极点。
把k p 弹出栈顶,扫描线仍然
停留在l p 上。
在下一轮处理中,将由i p 、j p 、l p 进行判断和作出处理。
3
格雷厄姆扫描法的实现
一、算法步骤:
1. 求平面点集S 中Y 坐标最小的点0p ;
2. 以0p 为源点,变换}{0p S -中所有点的坐标;
3. 以0p 为源点,计算}{0p S -中所有点的幅角;
4. 以幅角的非降顺序排序}{0p S -中所有的点,令事件调度点},,,{121-=n p p p T Λ是排序过的数组;
5. 初始化堆栈:令1]0[-=n p CS ,0]1[p CS =;令堆栈指针1=sp ,事件调度点数组T 的下标0=k ;
6. 如果1-<n k ,转7;否则,算法结束;
7. 按(11.1.7)式计算]1[-sp CS ,][sp CS ,][k T 所构成的三角区符号D ,若0≥D ,1+=sp sp ,][][k T sp CS =,1+=k k ;
转6;否则,1-=sp sp ;转6;
二、数据结构:
typedef struct {
float x; /* X 坐标 */
float y; /* Y 坐标 */
float ang; /* 极坐标的幅角 */
} SPOINT;
POINT S[n]; /* 平面点集 */
SPOINT T[n]; /* 按幅角的非降顺序排序的平面点集 */
POINT CS[n]; /* 构成凸壳的点集 */
三、算法的实现:
算法11.2 求平面点集的凸壳
输入:平面点集S[],顶点个数n
输出:构成凸壳的极点CS[];极点个数sp
1. void convex_hull(POINT S[],POINT CS[],int
n,int &sp)
2. {
3. int i,k;
4. float D;
5. SPOINT T[n];
6. for (i=1;i<n;i++)
/* S中Y坐标最小的点于S[0]*/
7.if
(S[i].y<S[0].y)||((S[i].y==S[0].y)&&(S[i].x<S
[0].x)))
4
8. swap(S[i],S[0]);
9. for (i=1;i<n;i++) {
/* 以S[0] 为源点,变换S[i]的坐标于T[i] */
10. T[i-1].x = S[i].x – S[0].x;
T[i].y = S[i].y – S[0].y;
11. T[i-1].ang = atan(T[i-1].y,T[i-1].x); /* 求T[i]的幅角 */
12. }
13. sort(T,n-1);
/* 按T[i]幅角的非降顺序排序T[i] */
14. CS[0].x = T[n-2].x; CS[0].y = T[n-2].y;
15. CS[1].x = 0; CS[1].y = 0; sp = 1; k = 0;
16. while (k<n-1) {
/* 求栈顶两点及扫描线上一点所构成的三角区符号 */ 17. D = CS[sp-1].x*CS[sp].y + CS[sp].x*T[k].y + T[k].x*CS[sp-1].y
18. - CS[sp-1].y*CS[sp].x - CS[sp].y*T[k].x - T[k].y*CS[sp-1].x;
19. if (D>=0)
5
20. CS[++sp] = T[k++];
/* 若D≥0,T[k]压入栈顶,扫描线前移一点 */
21. else sp--; /* 否则,弹出栈顶元素 */
22. }
23. }
四、复杂性分析
1、数据复杂性:
第6~8行及9~12行,各需)
Θ时间;
(n
第13行的排序操作,需)
O时间;
n
log
(n
第16~22行的while循环,需)
Θ时间。
(n
因此,算法的时间复杂性是)
O。
n
log
(n
2、空间复杂性:
用于输入的平面点集、用于输出的凸壳极点需要)
Θ空间
(n 事件调度点数组,需要)
(n
Θ空间。
6。