假设平面上给出N个点,现在要求过上面的点把所有的点都包起来,并且周长最小,这些点就是凸包上的点。

确定凸包上的点有多种做法,但是只有Graham扫描时间复杂度稳定在nlog(n)上,所以就记录一下这个算法。

**步骤:**

1.找出给定点中最靠近左下方的点

2.通过这个点与其它点连线与水平方向会构成夹角,根据夹角大小进行由小到大排序

3.根据动图中的情况,来说明扫描过程。

Graham扫描过程

凸多边形的边从最左下角点开始,逆时针的相邻三个点P0,P1,P2构成的向量,的夹角都是大于0的。也就是说,针对途中的情况,P2,P4,P5点是不可能在凸包上的。

假设P4在凸包上,那么对于P3,P4,P6来说,,向量的的夹角是小于0的,最后会呈现凹多边形的形态。

以P0,P1,P2,P3,P4,P5,P6来说明确定凸包点的计算方式:

(1)使用栈来存储最终位于凸包上的点的位置。

(2)最开始排好顺序的三个点P0,P1,P2一定能够组成凸多边形,将它们压入栈中S[P2,P1,P0],并且P1一定在凸包上,现在不确定的就是P2是否真的在凸包上(有内鬼? ::aru:discovertruth:: )

(3)对于P3来说,我们使用堆栈中第二个元素P1,第一个元素P2,和扫描到的元素P3,进行比较,来确认P2到底是不是凸包上的点。,向量构成的夹角小于0,说明P2一定不在凸包上,所以P2可以排除了,将P2从栈中弹出。P3又有可能是凸包上的点,所以堆栈现在存有S[P3,P1,P0]。

(4)对于P4来说,,向量**(,)**构成的夹角是大于0的,所以P4有可能是凸包上的点。

(5)当前栈中的点S[P4,P3,P1,P0]。对于P5来说,,**(,)**构成的夹角大于0,所以P5有可能是凸包上的点,所以对战现在存有S[P5,P4,P3,P1,P0]。

(6)对于P6来说,,**(,)**构成的夹角小于0,所以P5不可能是凸包上的点。将P5从栈中弹出。栈中元素为S[P4,P3,P1,P0]。再次执行判断,构成的夹角是否小于0,如果仍旧小于0,再将栈中的元素弹出.....直到能够确定P6是凸包上的点。

实际上就是回溯算法。

下面根据上述描述的过程,贴一下java代码的实现。

(这是POJ的1113号题目,题目链接:Wall。POJ用的是jdk1.5 太老了,所以下面的代码编译会报错...扎心)

import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStreamReader;

import java.util.Arrays;

import java.util.Comparator;

import java.util.LinkedList;

import java.util.StringTokenizer;

public class Main

{

public static void main(String[] args) throws IOException

{

final BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

StringTokenizer st = new StringTokenizer(br.readLine());

final int N = Integer.parseInt(st.nextToken());

final int L = Integer.parseInt(st.nextToken());

final int[][] point = new int[N][2];

for (int i = 0; i < N; i++)

{

st = new StringTokenizer(br.readLine());

point[i][0] = Integer.parseInt(st.nextToken());

point[i][1] = Integer.parseInt(st.nextToken());

}

final LinkedList targetPointsPosition = findPoints(point);

double result = 0;

final int size = targetPointsPosition.size();

for (int i = 0; i < size; i++)

{

if (i == size - 1)

{

result += getDistance(

becomeVector(point[targetPointsPosition.get(i)], point[targetPointsPosition.get(0)]));

continue;

}

result += getDistance(

becomeVector(point[targetPointsPosition.get(i)], point[targetPointsPosition.get(i + 1)]));

}

result += 2 * Math.PI * L;

System.out.println(Math.round(result));

}

private static LinkedList findPoints(int[][] point)

{

final int[] targetPointsPosition = new int[point.length];

final int[] leftBottom = getFirstPoint(point);

// 对点进行排序预处理

Arrays.sort(point, new Comparator() {

@Override

public int compare(int[] ints, int[] t1)

{

final int[] o1 = becomeVector(leftBottom, ints);

final int[] o2 = becomeVector(leftBottom, t1);

// 比较角度的结果

final int compareResult = myCompare(leftBottom, ints, t1);

if (compareResult == 0)

{

// 角度相同的时候,将距离P0点近的排在前面

final double dist1Pow = Math.pow(o1[0], 2) + Math.pow(o1[1], 2);

final double dist2Pow = Math.pow(o2[0], 2) + Math.pow(o2[1], 2);

return dist1Pow - dist2Pow > 0 ? 1 : -1;

}

return compareResult;

}

});

// 执行点的扫描

final LinkedList stack = new LinkedList();

stack.push(0);

stack.push(1);

stack.push(2);

// 角度的正负使用向量的sin值,sin在(-Π,0)为负数,(0,Π)为正数----向量的点乘

int[] p0, p1, p2;

int symbolNum;

for (int i = 3; i < point.length; i++)

{

while (true)

{

final int checkPoint = stack.pop();

p0 = point[checkPoint];

p1 = point[stack.peek()];

p2 = point[i];

symbolNum = symbol(p0, p1, p1, p2);

if (symbolNum == 1)

{

stack.push(checkPoint);

break;

}

}

stack.push(i);

}

return stack;

}

private static int[] getFirstPoint(int[][] point)

{

int[] result = new int[]

{ Integer.MAX_VALUE, Integer.MAX_VALUE };

for (final int[] ints : point)

{

if (ints[0] < result[0])

{

result = ints;

} else if (ints[0] == result[0])

{

if (ints[1] < result[1])

{

result = ints;

}

}

}

return result;

}

// 构造向量 private static int[] becomeVector(int[] p0, int[] p1)

{

return new int[]

{ p1[0] - p0[0], p1[1] - p0[1] };

}

// 用于点的排序的比较器

private static int myCompare(int[] p0, int[] p1, int[] p2)

{

final int[] vector01 = becomeVector(p0, p1);

final int[] vector02 = becomeVector(p0, p2);

final double cos01 = vector01[1] / getDistance(vector01);

final double cos02 = vector02[1] / getDistance(vector02);

if (Math.abs(cos01 - cos02) < 1e-6) { return 0; } // cos在[0,Π]单调递减 return cos01 - cos02 > 0 ? -1 : 1;

}

// ,向量夹角---sin---向量叉乘

private static int symbol(int[] p0, int[] p1, int[] p2, int[] p3)

{

final int[] vector01 = becomeVector(p0, p1);

final int[] vector23 = becomeVector(p2, p3);

final int result = vector01[0] * vector23[1] - vector01[1] * vector23[0];

return result >= 0 ? 1 : -1;

}

private static double getDistance(int[] vector)

{

return Math.sqrt(Math.pow(vector[0], 2) + Math.pow(vector[1], 2));

}

}

java 凸包,确定凸包上的点—Graham扫描法—java实现相关推荐

  1. java培训班 跟不上_如果去了Java培训班跟不上进度怎么办?

    想要从事Java的同学总是有几个疑惑,学好Java要不要去培训班?去了Java培训班我跟不上怎么办?上了Java培训班就一定能找到工作吗?学Java需不需要上培训班,今天我们就来谈一谈如果去了Java ...

  2. java 如何跳到上一个循环_Java 代码优化 Java 如何写好 一个 for 循环

    Java 代码优化 Java 如何写好 一个 for 循环 Java 代码优化 Java 如何写好 一个 for 循环 看到这个标题可能吓一跳,观众可能会有如下的OS ......... 满脸的不屑, ...

  3. endorsed java_使用Java 9和Java 10在Eclipse上从Tomcat中删除java.endorsed.dirs

    我在IDE中将Eclipse 4.7.3a与Tomcat 9.0.4结合使用.我从Java 8升级到Java10.我更改JAVA_HOME为指向JDK 10安装.我进入Eclipse,并创建了一个新的 ...

  4. 中国专业开发者最多,最受 Web 服务青睐,Java 8 为最受欢迎版本 | 2020 年 Java 开发现状大调查...

    来源 | JetBrains官博 译者&责编 | 夕颜 出品 | CSDN(ID:CSDNnews) 今年,Java已经25岁"高龄"了,再次创下辉煌的里程碑.最近,Jet ...

  5. java经纬度凸包graham_凸包算法(Graham扫描法)详解

    先说下基础知识,不然不好理解后面的东西 两向量的X乘p1(x1,y1),p2(x2,y2) p1Xp2如果小于零则说明  p1在p2的逆时针方向 如果大于零则说明 p1在p2的顺时针方向 struct ...

  6. Graham扫描法求解二维凸包问题

    最近在LeetCode的每日一题和实验中接连遇到凸包问题,因为之前从来没写过,于是写这篇博客记录一下,内容部分参考每日一题的官方题解和算法导论.算法的具体描述以及代码实现多为个人理解,如有谬误还请指出 ...

  7. 凸包 Graham扫描法 TOJ 1255 Surround the TreesTOJ 3100 女生寝室的围墙

    凸包算法有很多种... 我就学了一种Graham扫描法..以不变应万变... 1.把所有点放在二维坐标系中,则纵坐标最小的点一定是凸包上的点,如图中的P0. 2.把所有点的坐标平移一下,使 P0 作为 ...

  8. 转 最小凸包算法(Convex Hull)(1)-Graham扫描法 -计算几何-算法导论

    原文地址:http://blog.csdn.net/suwei19870312/article/details/542281 基本问题: 平面上有n个点p1,p2, ..., pn, 要求求出一个面积 ...

  9. 凸包模板(分治 or Graham扫描法)

    问题概述:空间上有很多点,现在要用一个凸多边形将所有点全部包住,求哪些点在这个凸多边形上 输入样例:                                             对应输出: ...

最新文章

  1. git tag学习记录(二)
  2. 每日一皮:当你感觉上升瓶颈的时候,不妨换个环境...
  3. 正则化方法:数据增强、regularization、dropout
  4. obs多推流地址_(无人直播)教程利用OBS推流抖音直播电脑屏幕或PC游戏
  5. WordPress函数:get_sidebar(获取侧边栏)
  6. 真会省钱!苹果iPhone SE3首拆:电池、内存容量揭晓、基带属实缩水
  7. Android科大讯飞语音识别源码及API下载
  8. V.Replication and Sharding(创建主从数据库)
  9. Android学习资料整理
  10. TCP 和 UDP 区别
  11. 微信小程序下拉刷新不回弹
  12. 浙江省高校教师职称计算机考试成绩查询系统,浙江省高校招生考试信息管理系统...
  13. linux可变剪切分析,SpliceR:一个用RNA-Seq数据进行可变剪接分类和预测潜在编码区域的R包...
  14. 数据可视化分析框架 amCharts 5
  15. 游戏蓝牙耳机哪款好用?低延迟游戏蓝牙耳机推荐
  16. 五年级上册计算机教案闽教版,小学信息技术闽教版五年级上册第7课 认识因特网教案设计...
  17. 画论27 宋徽宗敕纂《宣和画谱》
  18. 湖南省第六届大学生计算机程序设计竞赛---弟弟的作业
  19. 实木地板被机器人弄成坑_实木地板被修家具的压出凹痕看了心里难过怎么办
  20. Linux下YVU420转MP4工具下载,yuv420p转jpg linux(纯C语言实现)

热门文章

  1. 如何冥想?2500年的智慧——荒岛十日记
  2. 谁开启了协同领域的潘多拉盒子?
  3. 神作!Python 入门神图,一图搞定!
  4. 计算机网络体系结构整理-第二单元IP技术
  5. 华为服务器2288H v3和V5关机不能启动处理
  6. Nike新款鞋子型錄 vgav wluj yltn
  7. 5G大规模天线基站下的多用户性能测试技术
  8. Python等编程语言学习资料分享
  9. Python求一元二次方程的根
  10. 影响IT人员未来发展的五个IT新技术方向