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

article/2025/7/5 10:29:19

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

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

**步骤:**

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

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

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

611aca28653f130733d8e69a930c5581.gifGraham扫描过程

凸多边形的边从最左下角点开始,逆时针的相邻三个点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));

}

}


http://chatgpt.dhexx.cn/article/3CbG9QrJ.shtml

相关文章

平面凸包详解

注&#xff1a;这原本是一篇日报&#xff0c;但由于没通过一怒之下交成题解&#xff08;大雾 0.前言&#xff1a; 本篇文章&#xff0c;是一位 OI 选手无私的馈赠。两道凸包例题&#xff0c;五种凸包算法&#xff0c;涵盖了平面凸包从入门到进阶的大部分套路。作者相信&#…

凸包详解

首先讲解一下凸包的概念 用比较抽象的说就是&#xff1a; 在一个实数向量空间V中&#xff0c;对于给定集合X&#xff0c;所有包含X的凸集的交集S被称为X的凸包。X的凸包可以用X内所有点 (X1&#xff0c;...Xn)的凸组合来构造. 简单来说&#xff1a; 给你一个点集Q,你可以把…

凸包 —— 五种解法

欢迎访问https://blog.csdn.net/lxt_Lucia&#xff5e;&#xff5e; 宇宙第一小仙女\(^o^)/&#xff5e;&#xff5e;萌量爆表求带飞≡Σ((( つ^o^)つ~ dalao们点个关注呗&#xff5e;&#xff5e; 关于凸包&#xff0c;之前一直漏掉了这一个&#xff0c;上周末组队赛&#xf…

Python凸包

文章目录 ConvexHullQG三维情况ConvexHull属性 ConvexHull ConvexHull是spatial中的一个类&#xff0c;主要功能是找到一组点的边缘&#xff0c;并做一个凸包。其必要的初始化参数为一个点集&#xff0c;点集格式为 n m n\times m nm维度的数组&#xff0c;n为点集中点的个数…

30-凸包(Convex Hull)

文章目录 凸包&#xff08;convex hull&#xff09;凸包&#xff08;convex hull&#xff09;Graham扫描算法API使用步骤&#xff1a;Code效果 凸包&#xff08;convex hull&#xff09; 1、凸包概念&#xff1b; 2、API说明&#xff1b; 3、代码演示&#xff1b; convex &…

凸包(convex hull),凸包络面(convex envelope), 凸低估计量(convex underestimator), 图上方(epigraph),

凸分析中经常见到这些概念&#xff0c;目前这方面的中文资料似乎不太多&#xff0c;决定写篇博客总结一下。 文章目录 1. 凸包 convex hull2. 图上方 epigraph3. 凸低估计量 convex underestimator4. 凸包络面 convex envelope 1. 凸包 convex hull 凸包在文献中比较常见些&a…

凸包

一、引言 凸包&#xff08;Convex Hull&#xff09;是一个计算几何&#xff08;图形学&#xff09;中的概念。 在一个实数向量空间V中&#xff0c;对于给定集合X&#xff0c;所有包含X的凸集的交集S被称为X的凸包。X的凸包可以用X内所有点(X1&#xff0c;...Xn)的凸组合来构造…

凸包, 概念

catalog 凸包算法1 O(n * m)算法2 O(n * logN) Andrew安德鲁算法凸包模板 凸包 通常上讲 平面上有很多的点, 用一个 皮筋 圈住 这所有的点, 此时这个 皮筋, 就是 这些点的 凸包 数学上讲 性质1: 凸包上, 任何一条边 他所在的直线 即为L, 则所有的点 都在直线L 的 同一侧 比…

小白兔视频格式转换器下载

1、小白兔视频格式转换器 www.xxxbbbttt.com 上传你的视频&#xff08;腾讯qlv&#xff0c;爱奇艺qsv、优酷kux&#xff09;都可以。 2、点击转换按钮&#xff0c;转换好后&#xff0c;我们把转换的视频下载到电脑里&#xff0c;就可以看到视频已经是MP4格式了。

推荐一款能够将爱奇艺qsv、腾讯qlv、优酷kux完美转换成mp4的三合一全能格式转换器

爱奇艺视频、腾讯视频、优酷视频相信很多人都在使用其中的一款或者几款软件吧&#xff0c;这三大视频网站已经主导国内视频行业&#xff0c;都是购买视频版权。因此&#xff0c;为了保护视频版权&#xff0c;就将缓存视频的格式设置成专属的qsv、qlv、kux&#xff0c;这三种视频…

kux格式怎么转换成mp4?简单快速的视频转换技巧分享

现在生活压力越来越大,过完一天忙碌的工作回到家里开始煲剧是很多人的最爱。说到看视频,资源基本都被「三巨头」——腾讯视频、爱奇艺、优酷所覆盖,而有些朋友喜欢将视频下载下来看。可是却发现,下载的视频格式是kux、qsv、qlv,普通视频播放器根本打不开,这是怎么一回事呢…

怎么使用视频转换器把kux格式转换mp4

在周末或者平时闲暇的时间&#xff0c;很多人可能就是在家里看下视频吧&#xff1f;而且最好的方式&#xff0c;还是下载到本地上来观看&#xff0c;想要啥时候看就啥时候看&#xff0c;想要看哪个地方的就看哪个地方的。但是吧&#xff0c;有人发现了&#xff0c;自己下载的视…

上网成瘾改变大脑结构:语言功能受影响,让人话都说不利索

万博 发自 凹非寺量子位 | 公众号 QbitAI 经常不受控制地拿着手机或平板&#xff0c;漫无目的的刷微博&#xff0c;逛淘宝…… 如果有&#xff0c;那就需要注意了&#xff0c;这种无法自控的上网成瘾&#xff0c;是一种被叫做互联网使用障碍&#xff08;IUD&#xff09;的健康问…

数据统计分析的16个基础概念

来源&#xff1a;EasyShu 本文约11000字&#xff0c;建议阅读20分钟本文介绍了数据统计分析的16个基本概念。 一、描述统计 描述统计是通过图表或数学方法&#xff0c;对数据资料进行整理、分析&#xff0c;并对数据的分布状态、数字特征和随机变量之间关系进行估计和描述的方法…

睡眠剥夺后皮层微结构的广泛变化

大脑皮层的微观结构受到昼夜节律和睡眠剥夺的影响&#xff0c;但这些影响的确切基础尚不清楚。t1加权和t2加权磁共振图像之间的比率(T1w/T2w比率)与髓磷脂水平和树突密度有关&#xff0c;这可能为研究睡眠剥夺大脑的皮质内微观结构提供新的见解。在这里&#xff0c;我们检测了4…

常用的数据分析方法汇总

点击上方“小白学视觉”&#xff0c;选择加"星标"或“置顶” 重磅干货&#xff0c;第一时间送达 一、描述统计 描述统计是通过图表或数学方法&#xff0c;对数据资料进行整理、分析&#xff0c;并对数据的分布状态、数字特征和随机变量之间关系进行估计和描述的方法…

论文阅读:FLGCNN: A novel fully convolutional neural network for end-to-endmonaural speech enhancement

论文题目&#xff1a;FLGCNN: A novel fully convolutional neural network for end-to-end monaural speech enhancement with utterance-based objective functions 一种新颖的全卷积神经网络&#xff0c;用于具有基于话语的目标函数…

民安汇智量表科普!满意度调查量表怎么选?

满意度量表是进行满意度调查的主要工具和手段。目前的满意度量表因其适用的对象、范围和特点各异&#xff0c;存在着多种类型差异。每个量表都有自己的特点&#xff0c;没有一个量表能用于所有的客户满意度评价&#xff0c;哪怕是在同一期满意度调查&#xff0c;都会因地制宜&a…

一文介绍各种量表类型!

量表是一种测量工具&#xff0c;通常用来测量人们的主观态度、意见或价值观念。我们经常会在问卷中使用量表对调查对象进行测量&#xff0c;最常见到的就是李克特量表&#xff0c;关于量表还有很多的种类&#xff0c;这里具体介绍几种&#xff1a; 一. 李克特量表&#xff08;…

数据分析之数据预处理、分析建模、可视化

大纲 思维导图1. 数据分析概述1.1 简介1.2 发展历程1.3 应用领域1.4 开发流程 2. 数据类型2.1 结构化与非结构化数据2.2 定性与定量数据2.3 截面数据与时间序列数据 3. 数据来源4. 数据预处理方法4.1 数据清洗4.2 数据集成4.3 数据规约4.4 数据变换 5. 数据分析模型5.1 对比分析…