Graham Scan - Graham扫描算法


问题

用Graham Scan算法求拥有个点的点集的凸包,任意两点的坐标不同。

解法

在二维平面上选取坐标最小,坐标最小的顶点,将其余个顶点按照以的顺时针方向排序。如图所示:

GrahamScan1.png

上图中以点为基准,对其余点排序的结果为

根据Cross中向量叉积的知识,可知对于三个顶点组成的向量,设有以下情况:

(1) 若逆时针方向。如图:

GrahamScan2.png

(2) 若顺时针方向。如图:

GrahamScan3.png

(3) 若方向相同。对于向量完全相同的两顶点,按照到距离从近到远排序。如图:

GrahamScan4.png

排序时比较任意两点到的向量叉积都为负数,则整组顶点可以按照顺时针排列。设排列后的顶点顺序为

设置一个空堆栈,初始时推入三个顶点,此时堆栈为

然后遍历剩余顶点,对于每个顶点,考虑堆栈的头部顶点、次头部顶点,判断这三个点组成的两个向量,前者是否在后者的顺时针方向,即满足

(1) 若不满足条件,说明不是凸包上的点,对堆栈进行出栈操作(推出头部顶点)。然后再重复判断的值,直到满足该条件为止;

(2) 若满足条件,说明是凸包上的点,将其推入堆栈中。然后继续考虑下一个顶点

用伪代码来描述上述过程是

for i = 3 to n-1:
  while stack.size() >= 2 and ccw(stack.nextTop(), stack.top(), p[i]) <= 0:
    stack.pop()
  stack.push(p[i])
end

遍历完点集中所有顶点后,堆栈中的点即为的凸包。该算法的时间复杂度为


Introduction to Algorithms


源码

GrahamScan.h

GrahamScan.cpp

测试

GrahamScanTest.cpp