LeetCode-587 安装栅栏及三种凸包算法的学习

简介: LeetCode-587 安装栅栏及三种凸包算法的学习

来源:力扣(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;
    }
};
相关文章
|
28天前
|
存储 算法 Go
算法学习:数组 vs 链表
算法学习:数组 vs 链表
26 0
|
4天前
|
机器学习/深度学习 数据采集 算法
Python实现ISSA融合反向学习与Levy飞行策略的改进麻雀优化算法优化支持向量机回归模型(SVR算法)项目实战
Python实现ISSA融合反向学习与Levy飞行策略的改进麻雀优化算法优化支持向量机回归模型(SVR算法)项目实战
|
4天前
|
机器学习/深度学习 数据采集 算法
Python实现ISSA融合反向学习与Levy飞行策略的改进麻雀优化算法优化支持向量机分类模型(SVC算法)项目实战
Python实现ISSA融合反向学习与Levy飞行策略的改进麻雀优化算法优化支持向量机分类模型(SVC算法)项目实战
|
4天前
|
机器学习/深度学习 数据采集 监控
算法金 | DL 骚操作扫盲,神经网络设计与选择、参数初始化与优化、学习率调整与正则化、Loss Function、Bad Gradient
**神经网络与AI学习概览** - 探讨神经网络设计,包括MLP、RNN、CNN,激活函数如ReLU,以及隐藏层设计,强调网络结构与任务匹配。 - 参数初始化与优化涉及Xavier/He初始化,权重和偏置初始化,优化算法如SGD、Adam,针对不同场景选择。 - 学习率调整与正则化,如动态学习率、L1/L2正则化、早停法和Dropout,以改善训练和泛化。
4 0
算法金 | DL 骚操作扫盲,神经网络设计与选择、参数初始化与优化、学习率调整与正则化、Loss Function、Bad Gradient
|
17天前
|
存储 算法 搜索推荐
力扣每日一题 6/13 反悔贪心算法
力扣每日一题 6/13 反悔贪心算法
12 1
|
28天前
|
算法 Java
[Java·算法·简单] LeetCode 283. 移动零
[Java·算法·简单] LeetCode 283. 移动零
23 2
|
28天前
|
算法 搜索推荐 JavaScript
算法学习:快速排序
算法学习:快速排序
16 1
|
1月前
|
存储 算法 调度
力扣中级算法(Python)
力扣中级算法(Python)
|
1月前
|
算法 Python
力扣初级算法(Python)(二)
力扣初级算法(Python)(二)
|
9天前
|
Dart 算法 JavaScript
C#数据结构与算法入门教程,值得收藏学习!
C#数据结构与算法入门教程,值得收藏学习!