📜  使用分而治之算法的凸包

📅  最后修改于: 2021-10-23 08:20:49             🧑  作者: Mango

凸包是包含所有给定点的最小凸多边形。

凸包

输入是由 x 和 y 坐标指定的点数组。输出是这组点的凸包。

例子:

Input : points[] = {(0, 0), (0, 4), (-4, 0), (5, 0), 
                   (0, -6), (1, 0)};
Output : (-4, 0), (5, 0), (0, -6), (0, 4)

先决条件:
两个凸多边形之间的切线

算法:
给定一组点,我们必须找到凸包。假设我们知道左半点和右半点的凸包,那么现在的问题是合并这两个凸包,确定完整集的凸包。
这可以通过找到左右凸包的上下切线来完成。这在这里说明了两个凸多边形之间的切线

设左凸包为a,右凸包为b。然后将下切线和上切线分别命名为 1 和 2,如图所示。
然后红色轮廓显示了最终的凸包。

现在问题仍然存在,如何找到左半边和右半边的凸包。现在递归进入图片,我们划分点集,直到集合中的点数非常少,比如 5,我们可以通过 brute 算法找到这些点的凸包。这些半部分的合并将导致完整点集的凸包。

笔记:
我们已经使用 brute 算法找到少量点的凸包,它的时间复杂度为O(n^3) .但有人建议如下,3个或更少点的凸包是完整的点集。这是正确的,但是当我们尝试合并 2 个点的左凸包和 3 个点的右凸包时,问题就出现了,然后在某些特殊情况下程序会陷入无限循环。所以,为了摆脱这个问题,我直接找到了 5 个或更少点的凸包O(n^3)算法,稍微大一些,但不影响算法的整体复杂度。

// A divide and conquer program to find convex
// hull of a given set of points.
#include
using namespace std;
  
// stores the centre of polygon (It is made
// global because it is used in compare function)
pair mid;
  
// determines the quadrant of a point
// (used in compare())
int quad(pair p)
{
    if (p.first >= 0 && p.second >= 0)
        return 1;
    if (p.first <= 0 && p.second >= 0)
        return 2;
    if (p.first <= 0 && p.second <= 0)
        return 3;
    return 4;
}
  
// Checks whether the line is crossing the polygon
int orientation(pair a, pair b,
                pair c)
{
    int res = (b.second-a.second)*(c.first-b.first) -
              (c.second-b.second)*(b.first-a.first);
  
    if (res == 0)
        return 0;
    if (res > 0)
        return 1;
    return -1;
}
  
// compare function for sorting
bool compare(pair p1, pair q1)
{
    pair p = make_pair(p1.first - mid.first,
                                 p1.second - mid.second);
    pair q = make_pair(q1.first - mid.first,
                                 q1.second - mid.second);
  
    int one = quad(p);
    int two = quad(q);
  
    if (one != two)
        return (one < two);
    return (p.second*q.first < q.second*p.first);
}
  
// Finds upper tangent of two polygons 'a' and 'b'
// represented as two vectors.
vector> merger(vector > a,
                              vector > b)
{
    // n1 -> number of points in polygon a
    // n2 -> number of points in polygon b
    int n1 = a.size(), n2 = b.size();
  
    int ia = 0, ib = 0;
    for (int i=1; i a[ia].first)
            ia = i;
  
    // ib -> leftmost point of b
    for (int i=1; i=0)
            inda = (inda + 1) % n1;
  
        while (orientation(a[inda], b[indb], b[(n2+indb-1)%n2]) <=0)
        {
            indb = (n2+indb-1)%n2;
            done = 0;
        }
    }
  
    int uppera = inda, upperb = indb;
    inda = ia, indb=ib;
    done = 0;
    int g = 0;
    while (!done)//finding the lower tangent
    {
        done = 1;
        while (orientation(a[inda], b[indb], b[(indb+1)%n2])>=0)
            indb=(indb+1)%n2;
  
        while (orientation(b[indb], a[inda], a[(n1+inda-1)%n1])<=0)
        {
            inda=(n1+inda-1)%n1;
            done=0;
        }
    }
  
    int lowera = inda, lowerb = indb;
    vector> ret;
  
    //ret contains the convex hull after merging the two convex hulls
    //with the points sorted in anti-clockwise order
    int ind = uppera;
    ret.push_back(a[uppera]);
    while (ind != lowera)
    {
        ind = (ind+1)%n1;
        ret.push_back(a[ind]);
    }
  
    ind = lowerb;
    ret.push_back(b[lowerb]);
    while (ind != upperb)
    {
        ind = (ind+1)%n2;
        ret.push_back(b[ind]);
    }
    return ret;
  
}
  
// Brute force algorithm to find convex hull for a set
// of less than 6 points
vector> bruteHull(vector> a)
{
    // Take any pair of points from the set and check
    // whether it is the edge of the convex hull or not.
    // if all the remaining points are on the same side
    // of the line then the line is the edge of convex
    // hull otherwise not
    set >s;
  
    for (int i=0; i= 0)
                    pos++;
            }
            if (pos == a.size() || neg == a.size())
            {
                s.insert(a[i]);
                s.insert(a[j]);
            }
        }
    }
  
    vector>ret;
    for (auto e:s)
        ret.push_back(e);
  
    // Sorting the points in the anti-clockwise order
    mid = {0, 0};
    int n = ret.size();
    for (int i=0; i> divide(vector> a)
{
    // If the number of points is less than 6 then the
    // function uses the brute algorithm to find the
    // convex hull
    if (a.size() <= 5)
        return bruteHull(a);
  
    // left contains the left half points
    // right contains the right half points
    vector>left, right;
    for (int i=0; i>left_hull = divide(left);
    vector>right_hull = divide(right);
  
    // merging the convex hulls
    return merger(left_hull, right_hull);
}
  
// Driver code
int main()
{
    vector > a;
    a.push_back(make_pair(0, 0));
    a.push_back(make_pair(1, -4));
    a.push_back(make_pair(-1, -5));
    a.push_back(make_pair(-5, -3));
    a.push_back(make_pair(-3, -1));
    a.push_back(make_pair(-1, -3));
    a.push_back(make_pair(-2, -2));
    a.push_back(make_pair(-1, -1));
    a.push_back(make_pair(-2, -1));
    a.push_back(make_pair(-1, 1));
  
    int n = a.size();
  
    // sorting the set of points according
    // to the x-coordinate
    sort(a.begin(), a.end());
    vector >ans = divide(a);
  
    cout << "convex hull:\n";
    for (auto e:ans)
       cout << e.first << " "
            << e.second << endl;
  
    return 0;
}

输出:

Convex Hull:
-5 -3
-1 -5
1 -4
0 0
-1 1

时间复杂度:左右凸包的合并需要 O(n) 时间,因为我们将点分成相等的两部分,所以上述算法的时间复杂度为 O(n * log n)。

相关文章 :

  • 凸包 |设置 1(Jarvis 的算法或包装)
  • 凸包 |第 2 组(格雷厄姆扫描)
  • 凸包的 Quickhull 算法

如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程学生竞争性编程现场课程。