二维凸包问题

什么是凸包?

简单来说,可以想象在平面中有一些点,这些点的集合为 X,我们拿一个橡皮圈撑到最大,尝试套住所有的点,待橡皮圈绷紧后,它会成为一个多边形,这个多边形所有顶点组成的集合便为集合X 的凸包。如图:
一种凸包
凸包计算有很多办法,我在本文讲解Graham 扫描法。

算法实现:

  1. 找到一个一定在凸包中的初始点,比如最左下的点
  2. 按逆时针逐个加入可能在凸包中的点。做法就是先把剩下的点集按照相对初始点的极角排序,如果极角相同,则按距离排序,近的在前
  3. 从初始点开始,按顺序不断加入凸包,对于加入的每一个点,判断加入该点后,会不会使凸包呈现一种前一个点往内凹陷的形状,如果是这样的,则从凸包中删除前一个点,继续往前判断(怎么判断形状呢?利用叉乘)

下面详细解释一下算法实现

利用叉乘

对于平面中两个向量a=(Xa,Ya),b=(Xb,Yb),它们的叉乘也是一个向量,定义为c=(0,0,XaYb-XbYa),垂直于这个平面,符合右手定则。
首先,我们可以利用这个办法来实现算法的第 2 步,排序。所谓按极角顺序排序,就是将剩下点集与初始点组成的向量按逆时针排列,对其中两个向量做叉乘,如果结果为负,说明前一个向量对应的点的极角更小,于是把它排在前面,很容易完成。如果两向量共线,则按距离由小到大排,代码如下:

list.sort((a, b) -> {
    /* 按叉乘逆时针 */
	int diff = cross(list.get(0), a, b);
    if (diff == 0) {
    /* 按距离由近到远 */
    	return (distance(list.get(0), a) - distance(list.get(0), b));
    } else {
        return -diff;
    }
});

怎么利用叉乘的性质来判断删除哪些点呢?举如下例子:

初始点显然是A点,剩下的点按照上述算法描述的排序顺序标号,从 B 点开始不断加入凸包,如图:

当加入 F 点后,我们要找前两个加入的点来做叉乘,如果 DF相对于DE是顺时针的,那么 E 点就会往内凹陷,应该从凸包中删除 E 点

删除这一个点并不够。我们还要找凸包中 E 点之前的两个点 C 点和 D 点,CF相对于CD是顺时针的,于是再把 D 点删除,接下来找凸包中 D 点之前的两个点 B 点和 C 点,发现BF相对于BC是逆时针的,于是 C 点满足凸包性质,则 C 点之前的点也一定满足凸包性质,就不需要在做叉乘判断了,继续往后加点,重复这个判断即可。

对于加入的每个点,都要与凸包中最近加入的两个点做叉乘运算,很显然,这里可以用栈结构来保存凸包中的点,代码如下:

Deque<Integer> stack = new ArrayDeque<Integer>();
stack.push(0);
stack.push(1);
int n = list.size();
for (int i = 2; i < n; i++) {
    int top = stack.pop();
    /* 如果当前元素与栈顶的两个元素构成的向量顺时针旋转或共线,则弹出栈顶元素 */
    while (!stack.isEmpty() && cross(list.get(stack.peek()), list.get(top), list.get(i)) <= 0) {
        top = stack.pop();
    }
    stack.push(top);
    stack.push(i);
}

细节:距离判断

为什么对于共线的点,要按它们与初始点的距离从小到大排序呢?

考虑如下例子:

A、B、C、D 四点共线,算法会先加入 A、B 两点,当加入 C 时,AB与AC共线,则会踢掉 B 点,以此类推,最后只会保留 D 点。

反之,如果不按距离由小到大排序,假如先加入 D 点,则下一步加入 C 点时,算法判断AC与AD共线,则会踢掉 D 点,那么就会得到错误的答案。

  • 8
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值