来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/erect-the-fence

题目描述

在一个二维的花园中,有一些用 (x, y) 坐标表示的树。由于安装费用十分昂贵,你的任务是先用最短的绳子围起所有的树。只有当所有的树都被绳子包围时,花园才能围好栅栏。你需要找到正好位于栅栏边界上的树的坐标。

示例 1:

输入: [[1,1],[2,2],[2,0],[2,4],[3,3],[4,2]]
输出: [[1,1],[2,0],[4,2],[3,3],[2,4]]
解释:

示例 2:

输入: [[1,2],[2,2],[4,2]]
输出: [[1,2],[2,2],[4,2]]
解释:

即使树都在一条直线上,你也需要先用绳子包围它们。

注意:

所有的树应当被围在一起。你不能剪断绳子来包围树或者把树分成一组以上。
输入的整数在 0 到 100 之间。
花园至少有一棵树。
所有树的坐标都是不同的。
输入的点没有顺序。输出顺序也没有要求。

解题思路

第一次遇到这种类型的题,正好借此机会学习了三种凸包算法:

Jarvis算法:Jarvis算法的原理十分的简单,首先,对于一堆点,找到边缘处的点。

如图,我找到了最左侧的点A,这个点在边缘,所以一定会在栅栏上面,然后随笔选取一点B做向量AB

接下来,遍历每一个点,找到向量AB最右侧的点C(关于如何找最右侧,可以使用向量叉乘,如果叉乘和小于0,那么说明AC是AB顺时针旋转得到的,所以点C肯定会在向量AB的右侧,将C点当做B点继续遍历其他的点,就可以找到向量AB最右侧的点C了,注意,利用向量叉乘会出现0的情况,细节如何处理下文会介绍)

点AC就可以确定是外轮廓线,也就是栅栏的位置,接下来使用C作为起点重命名为A继续上述的步骤,直到C回到起始点A为止,外轮廓也就是栅栏就确定下来了。

在这个过程中会有一些特殊的情况,入下图

如果起始点从A遍历到B的时候,下一个点如果选中的是A,那么利用叉乘求最右侧点时候,如果先遍历的是C,那么就会出现叉乘为0而漏掉C的情况,在某些情况还会出现死循环,所以第一个选择的点需要一些技巧,这里每次指定这个点是数组中循环的下一个点而不是每次从数组第一个点开始遍历。

但是当起点是A点时候,如果第一个点选中的是C, 由于AB和AC叉乘为0,所以B点会被漏选,所以在每次选中最右点后,要判断是否有点与起点和最右点共线,将这些点也应该加入篱笆。

此算法对于每个点都要遍历一次其余各点,所以时间复杂度为O(n2)。

代码展示

Jarvis算法:

class Solution {
public:
vector<vector<int>> outerTrees(vector<vector<int>>& trees) {
if(trees.size() < 4) return trees;
vector<vector<int>> vviRet;
unordered_set<int> setiVistied;
int iStart = 0, iCur = 0;
/* find leftest tree*/
for(int i = 0; i < trees.size(); i++)
{
if (trees[i][0] < trees[iStart][0])
{
iStart = i;
}
}
iCur = iStart;
do
{
int iNext = (iCur + 1) % trees.size();
int iDirX = trees[iNext][0] - trees[iCur][0];
int iDirY = trees[iNext][1] - trees[iCur][1];
for(int i = 0; i < trees.size(); i++)
{
int iTempX = trees[i][0] - trees[iCur][0];
int iTempY = trees[i][1] - trees[iCur][1];
if(iDirX * iTempY - iDirY * iTempX < 0)
{
iDirX = iTempX;
iDirY = iTempY;
iNext = i;
}
}
for(int i = 0; i < trees.size(); i++)
{
int iTempX = trees[i][0] - trees[iCur][0];
int iTempY = trees[i][1] - trees[iCur][1];
if(iDirX * iTempY - iDirY * iTempX == 0 && setiVistied.find(i) == setiVistied.end())
{
setiVistied.emplace(i);
vviRet.emplace_back(vector<int>{trees[i][0], trees[i][1]});
}
}
iCur = iNext;
}
while(iCur != iStart);
return vviRet;
}
};

运行结果

LeetCode-587 安装栅栏及三种凸包算法的学习的更多相关文章

  1. Java实现 LeetCode 587 安装栅栏(图算法转换成数学问题)

    587. 安装栅栏 在一个二维的花园中,有一些用 (x, y) 坐标表示的树.由于安装费用十分昂贵,你的任务是先用最短的绳子围起所有的树.只有当所有的树都被绳子包围时,花园才能围好栅栏.你需要找到正好 ...

  2. Leetcode 587.安装栅栏

    安装栅栏 在一个二维的花园中,有一些用 (x, y) 坐标表示的树.由于安装费用十分昂贵,你的任务是先用最短的绳子围起所有的树.只有当所有的树都被绳子包围时,花园才能围好栅栏.你需要找到正好位于栅栏边 ...

  3. 7.1 安装软件包的三种方法 7.2 rpm包介绍 7.3 rpm工具用法 7.4 yum工具用法 7.5 yum搭建本地仓库

    7.1 安装软件包的三种方法 7.2 rpm包介绍 7.3 rpm工具用法 7.4 yum工具用法 7.5 yum搭建本地仓库 三种方法 rpm工具----->类型windows下的exe程序 ...

  4. 安装软件包的三种方法、RPM包介绍、rpm、yum工具用法、yum搭建本地仓库

    第5周第3次课(4月18日) 课程内容: 7.1 安装软件包的三种方法7.2 rpm包介绍7.3 rpm工具用法7.4 yum工具用法7.5 yum搭建本地仓库 7.1 安装软件包的三种方法 rpm工 ...

  5. Linux CentOS7 VMware 安装软件包的三种方法、rpm包介绍、rpm工具用法、yum工具用法、yum搭建本地仓库

    一.安装软件包的三种方法 Linux下游三种安装方法,rpm工具.yum工具.源码包.rpm按装一个程序包时,有可能因为该程序包依赖另一个程序包而无法安装:yum工具,可以连同依赖的程序包一起安装. ...

  6. FIFO、LRU、OPT这三种置换算法的缺页次数

    考虑下述页面走向: 1,2,3,4,2,1,5,6,2,1,2,3,7,6,3,2,1,2,3,6 当内存块数量分别为3时,试问FIFO.LRU.OPT这三种置换算法的缺页次数各是多少? 答:缺页定义 ...

  7. 排序—时间复杂度为O(n2)的三种排序算法

    1 如何评价.分析一个排序算法? 很多语言.数据库都已经封装了关于排序算法的实现代码.所以我们学习排序算法目的更多的不是为了去实现这些代码,而是灵活的应用这些算法和解决更为复杂的问题,所以更重要的是学 ...

  8. 基于C#程序设计语言的三种组合算法

    目录 基于C#程序设计语言的三种组合算法 1. 总体思路 1.1 前言 1.2 算法思路 1.3 算法需要注意的点 2. 三种组合算法 2.1 普通组合算法 2.2 与自身进行组合的组合算法 2.3 ...

  9. 网络中,FIFO、LRU、OPT这三种置换算法的缺页次数

    FIFO.LRU.OPT这三种置换算法的缺页次数 转载  由于要考计算机四级网络,这里遇到了问题,就搜了一些资料来解疑. 考虑下述页面走向: 1,2,3,4,2,1,5,6,2,1,2,3,7,6,3 ...

  10. 三种Hash算法对比以及秒传原理.

    三种Hash算法对比以及秒传原理 CRC (32/64)   MD5  Sha1 分5个点来说 1.校验值长度 2.校验值类别 3.安全级别 4.应用场景 1).校验值长度 CRC(32/64) 分别 ...

随机推荐

  1. 关于Window中右键新建菜单的设置

    在日常工作中,有时候需要根据自己的需要对Windows桌面右键新建菜单进行自定义,自定义的方法如下: 1.在Window中输入Win+R调出运行命令框 2.输入regedit,打开注册表 3.如果想要 ...

  2. 使用python批量更改文件

    最近整理之前学爬虫存储的文件,发现有很多文件名有重复,而我有一点点强迫症,不想文件名重复,就写了一个Python代码来解决文件名重复问题 import os import random import ...

  3. [数据与分析可视化] D3入门教程3-d3中的数据操作

    d3.js入门教程3-d3.js中的数据操作 文章目录 d3.js入门教程3-d3.js中的数据操作 数学操作 对象和数组 过滤Filtering 排序Sorting 映射group 循环loop 重 ...

  4. ING国际银行基于Volcano的大数据分析平台应用实践

    摘要:ING集团发表了<Efficient Scheduling Of High Performance Batch Computing For Analytics Workloads With ...

  5. ionic+vue+capacitor系列笔记--03项目使用Native插件

    话不多说,直接上代码 下载依赖 npm install @capacitor/camera 添加权限配置代码到安卓文件夹里的 AndroidManifest.xml <uses-permissi ...

  6. 解决node.js报错Invalid character in header content ["Content-Disposition"]

    遇到这种报错一般在于下载文件时候,如果Content-Disposition设置文件名有中文会出现此种问题,解决方案如下: 把第二段代码改为第一段,即可~~ 'Content-Disposition' ...

  7. Java入门与进阶 P4.1+P4.2

    表达积累的结果应该初始化为1 for循环 for循环像一个计数循环:设定一个计数器,初始化它,然后再计数器到达某值之前,重复执行循环体,而每执行一轮循环,计数器值以一定步骤进行调整,比如加 i 或者减 ...

  8. java 进阶P-6.2+P-6.3

    细胞自动机 CellularAutomation(细胞自动机)细胞自动机(英语:Cellular automaton),又称格状自动机.元胞自动机,是一种离散模型,在可算性理论.数学及理论生物学都有相 ...

  9. elasticsearch中使用runtime fields

    1.背景 在我们使用es的开发过程中可能会遇到这么一种情况,比如我们的线路名称字段lineName字段在设置mapping的时候使用的是text类型,但是后期发现需要使用这个字段来进行聚合操作,那么我 ...

  10. 数据湖Hudi与对象存储Minio及Hive\Spark\Flink的集成

    本文主要记录对象存储组件Minio.数据湖组件Hudi及查询引擎Hive\Spark之间的兼容性配置及测试情况,Spark及Hive无需多言,这里简单介绍下Minio及Hudi. MinIO 是在 G ...