📜  凸包|设置1(贾维斯算法或包装)

📅  最后修改于: 2021-04-23 20:00:01             🧑  作者: Mango

给定平面上的一组点。该集合的凸包是包含其所有点的最小凸多边形。

强烈建议您先阅读以下文章。
如何检查两个给定的线段是否相交?

Jarvis算法的思想很简单,我们从最左边的点(或x坐标值最小的点)开始,并沿逆时针方向包裹点。最大的问题是,给定点p作为当前点,如何找到输出中的下一个点?这个想法是在这里使用direction()。选择下一个点作为在逆时针方向上击败所有其他点的点,即,如果对于任何其他点r,下一个点是q,则我们具有“ orientation(p,q,r)=逆时针方向”。以下是详细的算法。

1)将p初始化为最左边的点。
2)在不回到第一个(或最左边)的时候做下面的事情。
…..a )下一个点q是这样的点,即对于任何其他点r,三元组(p,q,r)都是逆时针方向。为了找到这一点,我们只需将q初始化为下一个点,然后遍历所有点。对于任何点i,如果i更加逆时针旋转,即,direction(p,i,q)逆时针旋转,则将q更新为i。 q的最终值将是最逆时针的点。
….. b) next [p] = q(将q作为p的下一个存储在输出凸包中)。
….. c) p = q(将p设为下一次迭代的q)。

下面是上述算法的实现。

C++
// A C++ program to find convex hull of a set of points. Refer
// https://www.geeksforgeeks.org/orientation-3-ordered-points/
// for explanation of orientation()
#include 
using namespace std;
  
struct Point
{
    int x, y;
};
  
// To find orientation of ordered triplet (p, q, r).
// The function returns following values
// 0 --> p, q and r are colinear
// 1 --> Clockwise
// 2 --> Counterclockwise
int orientation(Point p, Point q, Point r)
{
    int val = (q.y - p.y) * (r.x - q.x) -
              (q.x - p.x) * (r.y - q.y);
  
    if (val == 0) return 0;  // colinear
    return (val > 0)? 1: 2; // clock or counterclock wise
}
  
// Prints convex hull of a set of n points.
void convexHull(Point points[], int n)
{
    // There must be at least 3 points
    if (n < 3) return;
  
    // Initialize Result
    vector hull;
  
    // Find the leftmost point
    int l = 0;
    for (int i = 1; i < n; i++)
        if (points[i].x < points[l].x)
            l = i;
  
    // Start from leftmost point, keep moving counterclockwise
    // until reach the start point again.  This loop runs O(h)
    // times where h is number of points in result or output.
    int p = l, q;
    do
    {
        // Add current point to result
        hull.push_back(points[p]);
  
        // Search for a point 'q' such that orientation(p, q,
        // x) is counterclockwise for all points 'x'. The idea
        // is to keep track of last visited most counterclock-
        // wise point in q. If any point 'i' is more counterclock-
        // wise than q, then update q.
        q = (p+1)%n;
        for (int i = 0; i < n; i++)
        {
           // If i is more counterclockwise than current q, then
           // update q
           if (orientation(points[p], points[i], points[q]) == 2)
               q = i;
        }
  
        // Now q is the most counterclockwise with respect to p
        // Set p as q for next iteration, so that q is added to
        // result 'hull'
        p = q;
  
    } while (p != l);  // While we don't come to first point
  
    // Print Result
    for (int i = 0; i < hull.size(); i++)
        cout << "(" << hull[i].x << ", "
              << hull[i].y << ")\n";
}
  
// Driver program to test above functions
int main()
{
    Point points[] = {{0, 3}, {2, 2}, {1, 1}, {2, 1},
                      {3, 0}, {0, 0}, {3, 3}};
    int n = sizeof(points)/sizeof(points[0]);
    convexHull(points, n);
    return 0;
}


Java
// Java program to find convex hull of a set of points. Refer 
// https://www.geeksforgeeks.org/orientation-3-ordered-points/
// for explanation of orientation()
import java.util.*;
  
class Point
{
    int x, y;
    Point(int x, int y){
        this.x=x;
        this.y=y;
    }
}
  
class GFG {
      
    // To find orientation of ordered triplet (p, q, r).
    // The function returns following values
    // 0 --> p, q and r are colinear
    // 1 --> Clockwise
    // 2 --> Counterclockwise
    public static int orientation(Point p, Point q, Point r)
    {
        int val = (q.y - p.y) * (r.x - q.x) -
                  (q.x - p.x) * (r.y - q.y);
       
        if (val == 0) return 0;  // collinear
        return (val > 0)? 1: 2; // clock or counterclock wise
    }
      
    // Prints convex hull of a set of n points.
    public static void convexHull(Point points[], int n)
    {
        // There must be at least 3 points
        if (n < 3) return;
       
        // Initialize Result
        Vector hull = new Vector();
       
        // Find the leftmost point
        int l = 0;
        for (int i = 1; i < n; i++)
            if (points[i].x < points[l].x)
                l = i;
       
        // Start from leftmost point, keep moving 
        // counterclockwise until reach the start point
        // again. This loop runs O(h) times where h is
        // number of points in result or output.
        int p = l, q;
        do
        {
            // Add current point to result
            hull.add(points[p]);
       
            // Search for a point 'q' such that 
            // orientation(p, q, x) is counterclockwise 
            // for all points 'x'. The idea is to keep 
            // track of last visited most counterclock-
            // wise point in q. If any point 'i' is more 
            // counterclock-wise than q, then update q.
            q = (p + 1) % n;
              
            for (int i = 0; i < n; i++)
            {
               // If i is more counterclockwise than 
               // current q, then update q
               if (orientation(points[p], points[i], points[q])
                                                   == 2)
                   q = i;
            }
       
            // Now q is the most counterclockwise with
            // respect to p. Set p as q for next iteration, 
            // so that q is added to result 'hull'
            p = q;
       
        } while (p != l);  // While we don't come to first 
                           // point
       
        // Print Result
        for (Point temp : hull)
            System.out.println("(" + temp.x + ", " +
                                temp.y + ")");
    }
      
    /* Driver program to test above function */
    public static void main(String[] args) 
    {
  
        Point points[] = new Point[7];
        points[0]=new Point(0, 3);
        points[1]=new Point(2, 3);
        points[2]=new Point(1, 1);
        points[3]=new Point(2, 1);
        points[4]=new Point(3, 0);
        points[5]=new Point(0, 0);
        points[6]=new Point(3, 3);
          
        int n = points.length;
        convexHull(points, n);
         
    }
}
    
// This code is contributed by Arnav Kr. Mandal.


Python3
# C# program to find convex hull of a set of points. Refer 
# https://www.geeksforgeeks.org/orientation-3-ordered-points/
# for explanation of orientation()
  
# point class with x, y as point 
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
  
def Left_index(points):
      
    '''
    Finding the left most point
    '''
    minn = 0
    for i in range(1,len(points)):
        if points[i].x < points[minn].x:
            minn = i
        elif points[i].x == points[minn].x:
            if points[i].y > points[minn].y:
                minn = i
    return minn
  
def orientation(p, q, r):
    '''
    To find orientation of ordered triplet (p, q, r). 
    The function returns following values 
    0 --> p, q and r are colinear 
    1 --> Clockwise 
    2 --> Counterclockwise 
    '''
    val = (q.y - p.y) * (r.x - q.x) - \
          (q.x - p.x) * (r.y - q.y)
  
    if val == 0:
        return 0
    elif val > 0:
        return 1
    else:
        return 2
  
def convexHull(points, n):
      
    # There must be at least 3 points 
    if n < 3:
        return
  
    # Find the leftmost point
    l = Left_index(points)
  
    hull = []
      
    '''
    Start from leftmost point, keep moving counterclockwise 
    until reach the start point again. This loop runs O(h) 
    times where h is number of points in result or output. 
    '''
    p = l
    q = 0
    while(True):
          
        # Add current point to result 
        hull.append(p)
  
        '''
        Search for a point 'q' such that orientation(p, q, 
        x) is counterclockwise for all points 'x'. The idea 
        is to keep track of last visited most counterclock- 
        wise point in q. If any point 'i' is more counterclock- 
        wise than q, then update q. 
        '''
        q = (p + 1) % n
  
        for i in range(n):
              
            # If i is more counterclockwise 
            # than current q, then update q 
            if(orientation(points[p], 
                           points[i], points[q]) == 2):
                q = i
  
        '''
        Now q is the most counterclockwise with respect to p 
        Set p as q for next iteration, so that q is added to 
        result 'hull' 
        '''
        p = q
  
        # While we don't come to first point
        if(p == l):
            break
  
    # Print Result 
    for each in hull:
        print(points[each].x, points[each].y)
  
# Driver Code
points = []
points.append(Point(0, 3))
points.append(Point(2, 2))
points.append(Point(1, 1))
points.append(Point(2, 1))
points.append(Point(3, 0))
points.append(Point(0, 0))
points.append(Point(3, 3))
  
convexHull(points, len(points))
  
# This code is contributed by 
# Akarsh Somani, IIIT Kalyani


C#
// C# program to find convex hull of a set of points. Refer 
// https://www.geeksforgeeks.org/orientation-3-ordered-points/
// for explanation of orientation()
using System;
using System.Collections.Generic; 
      
public class Point
{
    public int x, y;
    public Point(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
}
  
public class GFG 
{
      
    // To find orientation of ordered triplet (p, q, r).
    // The function returns following values
    // 0 --> p, q and r are colinear
    // 1 --> Clockwise
    // 2 --> Counterclockwise
    public static int orientation(Point p, Point q, Point r)
    {
        int val = (q.y - p.y) * (r.x - q.x) -
                (q.x - p.x) * (r.y - q.y);
      
        if (val == 0) return 0; // collinear
        return (val > 0)? 1: 2; // clock or counterclock wise
    }
      
    // Prints convex hull of a set of n points.
    public static void convexHull(Point []points, int n)
    {
        // There must be at least 3 points
        if (n < 3) return;
      
        // Initialize Result
        List hull = new List();
      
        // Find the leftmost point
        int l = 0;
        for (int i = 1; i < n; i++)
            if (points[i].x < points[l].x)
                l = i;
      
        // Start from leftmost point, keep moving 
        // counterclockwise until reach the start point
        // again. This loop runs O(h) times where h is
        // number of points in result or output.
        int p = l, q;
        do
        {
            // Add current point to result
            hull.Add(points[p]);
      
            // Search for a point 'q' such that 
            // orientation(p, q, x) is counterclockwise 
            // for all points 'x'. The idea is to keep 
            // track of last visited most counterclock-
            // wise point in q. If any point 'i' is more 
            // counterclock-wise than q, then update q.
            q = (p + 1) % n;
              
            for (int i = 0; i < n; i++)
            {
            // If i is more counterclockwise than 
            // current q, then update q
            if (orientation(points[p], points[i], points[q])
                                                == 2)
                q = i;
            }
      
            // Now q is the most counterclockwise with
            // respect to p. Set p as q for next iteration, 
            // so that q is added to result 'hull'
            p = q;
      
        } while (p != l); // While we don't come to first 
                        // point
      
        // Print Result
        foreach (Point temp in hull)
            Console.WriteLine("(" + temp.x + ", " +
                                temp.y + ")");
    }
      
    /* Driver code */
    public static void Main(String[] args) 
    {
  
        Point []points = new Point[7];
        points[0]=new Point(0, 3);
        points[1]=new Point(2, 3);
        points[2]=new Point(1, 1);
        points[3]=new Point(2, 1);
        points[4]=new Point(3, 0);
        points[5]=new Point(0, 0);
        points[6]=new Point(3, 3);
          
        int n = points.Length;
        convexHull(points, n);
          
    }
}
  
// This code is contributed by Princi Singh


输出:输出是凸包的点。

(0, 3)
(0, 0)
(3, 0)
(3, 3)


注意
:当凸包中存在共线点时,对于不同的输入顺序,上面的代码可能会产生不同的结果。例如,对于输入(0,3),(0,0),(0,1),(3, 0),(3、3)并以(0,3)(0,1)(0,0)(3,0)(3,3)输出为(0,3),(0,1) ,(0,0),(3,0),(3,3),在共线情况下,我们通常需要最远的下一个点;在共线点的情况下,我们可以通过再添加一个if条件来获得所需的结果。请参考此修改后的代码。

时间复杂度:对于船体上的每个点,我们都会检查所有其他点以确定下一个点。时间复杂度为?(m * n),其中n是输入点数,m是输出或船体点数(m <= n)。在最坏的情况下,时间复杂度为O(n 2 )。最坏的情况发生在所有点都在船体上时(m = n)

套装2-凸包(格雷厄姆扫描)

资料来源:
http://www.cs.uiuc.edu/~jeffe/teaching/373/notes/x05-convexhull.pdf
http://www.dcs.gla.ac.uk/~pat/52233/slides/Hull1x1.pdf