凸包问题
题目描述(leetcode-587)
在一个二维的花园中,有一些用 (x, y) 坐标表示的树。由于安装费用十分昂贵,你的任务是先用最短的绳子围起所有的树。只有当所有的树都被绳子包围时,花园才能围好栅栏。你需要找到正好位于栅栏边界上的树的坐标。
注意:
- 所有的树应当被围在一起。你不能剪断绳子来包围树或者把树分成一组以上。
- 输入的整数在 0 到 100 之间。
- 花园至少有一棵树。
- 所有树的坐标都是不同的。
- 输入的点没有顺序。输出顺序也没有要求
暴力解法
凸包一定是最外围的那些点圈成,所以假设有n个点,那么最多可以构造出n(n−1)/2
条边,算法如下:
- 选定一条边,遍历其他n-2个点,如果所有点都在该条边的一侧,则加入凸包集合。
- 不断重复步骤1,直到所有边都被遍历过。
显然,时间复杂度为O(n3)
1 | public List<Point> outerTrees(Point[] points) { |
其中pointPosition
用于判断点temp在直线p1p2的哪一侧,当返回结果为正时,temp在直线p1p2的左侧;当结果为负时,temp在直线p1p2的右边。
1 | private int pointPosition(Point p1, Point p2, Point temp) { |
分治法
在一个凸包中,横坐标最小和横坐标最大的点一定是凸包上的边界点。根据此性质,可以得到分治解法
算法步骤如下:
- 选取横坐标最小和最大的两个点P1和Pn,这两个点一定是凸包上的点。直线P1Pn把点集分成了两部分,即上面和下面两部分,分别叫做上凸包和下凸包。
- 对上凸包:求距离直线P1Pn最远的点Pmax
- 作直线P1Pmax、PnPmax,把直线 P1Pmax 左侧的点当成是上包,把直线 PnPmax 右侧的点也当成是上包。
- 重复步骤2、3。
- 对下凸包也作同样的操作
1 | Set<Point> result = new HashSet<>(); |
对子问题的处理:
1 | private void subOuterTrees(List<Point> points, boolean isUpConvex) { |
子集的划分:
1 | private void split(int index1, int index2, List<Point> points, List<Point> part, boolean isUpConvex) { |