c++ 使用 Graham 扫描的凸包(Convex Hull using Graham Scan)

devtools/2024/10/18 14:26:29/

先决条件:

如何检查两个给定的线段是否相交?

c++ https://blog.csdn.net/hefeng_aspnet/article/details/141713655
java https://blog.csdn.net/hefeng_aspnet/article/details/141713762
python https://blog.csdn.net/hefeng_aspnet/article/details/141714389
C# https://blog.csdn.net/hefeng_aspnet/article/details/141714420
Javascript https://blog.csdn.net/hefeng_aspnet/article/details/141714442 

贾维斯算法

 c++ https://blog.csdn.net/hefeng_aspnet/article/details/141716082
java https://blog.csdn.net/hefeng_aspnet/article/details/141716363
python https://blog.csdn.net/hefeng_aspnet/article/details/141716444
C# https://blog.csdn.net/hefeng_aspnet/article/details/141716403
Javascript https://blog.csdn.net/hefeng_aspnet/article/details/141716421 

        凸包是包含给定点集的最小凸多边形。它是计算几何学中一个有用的概念,在计算机图形学、图像处理和碰撞检测等各个领域都有应用。

凸多边形是所有内角都小于180度的多边形。可以为任意点集构造凸包,无论这些点如何排列。

格雷厄姆扫描算法
        格雷厄姆扫描算法是一种简单而有效的算法,用于计算一组点的凸包。它的工作原理是迭代地将点添加到凸包中,直到所有点都添加完毕。

        该算法首先找到 y 坐标最小的点。该点始终位于凸包上。然后,该算法根据剩余点相对于起点的极角对它们进行排序。

        然后,算法会迭代地将点添加到凸包中。在每个步骤中,算法都会检查添加到凸包中的最后两个点是否形成右转。如果是,则将最后一个点从凸包中移除。否则,将排序列表中的下一个点添加到凸包中。

当所有点都被添加到凸包时,算法终止。

Graham Scan的实现:

第一阶段(排序点):
        实现格雷厄姆扫描算法的第一步是按照点相对于起点的极角对点进行排序。
        一旦点排序完毕,起点就会添加到凸包中。一旦点排序完毕,它们就会形成一条简单的闭合路径(见下图)。 

第二阶段(接受或拒绝点):

    一旦我们有了闭合路径,下一步就是遍历该路径并移除该路径上的凹点。如何决定移除哪个点以及保留哪个点?同样,方向在这里有帮助。排序数组中的前两个点始终是凸包的一部分。对于剩余的点,我们跟踪最近的三个点,并找到它们形成的角度。让这三个点分别为prev(p)、curr(c)和next(n)。如果这些点的方向(按相同顺序考虑它们)不是逆时针的,我们丢弃 c,否则我们保留它。下图显示了此阶段的分步过程。
 

下面是上述算法的实现: 

// A C++ program to find convex hull of a set of points. Refer
//c++ https://blog.csdn.net/hefeng_aspnet/article/details/141713859
//java https://blog.csdn.net/hefeng_aspnet/article/details/141717851
//python https://blog.csdn.net/hefeng_aspnet/article/details/141717981
//C# https://blog.csdn.net/hefeng_aspnet/article/details/141718039
//Javascript https://blog.csdn.net/hefeng_aspnet/article/details/141718097
// for explanation of orientation()
#include <iostream>
#include <stack>
#include <stdlib.h>
using namespace std;

struct Point
{
    int x, y;
};

// A global point needed for  sorting points with reference
// to  the first point Used in compare function of qsort()
Point p0;

// A utility function to find next to top in a stack
Point nextToTop(stack<Point> &S)
{
    Point p = S.top();
    S.pop();
    Point res = S.top();
    S.push(p);
    return res;
}

// A utility function to swap two points
void swap(Point &p1, Point &p2)
{
    Point temp = p1;
    p1 = p2;
    p2 = temp;
}

// A utility function to return square of distance
// between p1 and p2
int distSq(Point p1, Point p2)
{
    return (p1.x - p2.x)*(p1.x - p2.x) +
          (p1.y - p2.y)*(p1.y - p2.y);
}

// To find orientation of ordered triplet (p, q, r).
// The function returns following values
// 0 --> p, q and r are collinear
// 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;  // collinear
    return (val > 0)? 1: 2; // clock or counterclock wise
}

// A function used by library function qsort() to sort an array of
// points with respect to the first point
int compare(const void *vp1, const void *vp2)
{
   Point *p1 = (Point *)vp1;
   Point *p2 = (Point *)vp2;

   // Find orientation
   int o = orientation(p0, *p1, *p2);
   if (o == 0)
     return (distSq(p0, *p2) >= distSq(p0, *p1))? -1 : 1;

   return (o == 2)? -1: 1;
}

// Prints convex hull of a set of n points.
void convexHull(Point points[], int n)
{
   // Find the bottommost point
   int ymin = points[0].y, min = 0;
   for (int i = 1; i < n; i++)
   {
     int y = points[i].y;

     // Pick the bottom-most or choose the left
     // most point in case of tie
     if ((y < ymin) || (ymin == y &&
         points[i].x < points[min].x))
        ymin = points[i].y, min = i;
   }

   // Place the bottom-most point at first position
   swap(points[0], points[min]);

   // Sort n-1 points with respect to the first point.
   // A point p1 comes before p2 in sorted output if p2
   // has larger polar angle (in counterclockwise
   // direction) than p1
   p0 = points[0];
   qsort(&points[1], n-1, sizeof(Point), compare);

   // If two or more points make same angle with p0,
   // Remove all but the one that is farthest from p0
   // Remember that, in above sorting, our criteria was
   // to keep the farthest point at the end when more than
   // one points have same angle.
   int m = 1; // Initialize size of modified array
   for (int i=1; i<n; i++)
   {
       // Keep removing i while angle of i and i+1 is same
       // with respect to p0
       while (i < n-1 && orientation(p0, points[i],
                                    points[i+1]) == 0)
          i++;


       points[m] = points[i];
       m++;  // Update size of modified array
   }

   // If modified array of points has less than 3 points,
   // convex hull is not possible
   if (m < 3) return;

   // Create an empty stack and push first three points
   // to it.
   stack<Point> S;
   S.push(points[0]);
   S.push(points[1]);
   S.push(points[2]);

   // Process remaining n-3 points
   for (int i = 3; i < m; i++)
   {
      // Keep removing top while the angle formed by
      // points next-to-top, top, and points[i] makes
      // a non-left turn
      while (S.size()>1 && orientation(nextToTop(S), S.top(), points[i]) != 2)
         S.pop();
      S.push(points[i]);
   }

   // Now stack has the output points, print contents of stack
   while (!S.empty())
   {
       Point p = S.top();
       cout << "(" << p.x << ", " << p.y <<")" << endl;
       S.pop();
   }
}

// Driver program to test above functions
int main()
{
    Point points[] = {{0, 3}, {1, 1}, {2, 2}, {4, 4},
                      {0, 0}, {1, 2}, {3, 1}, {3, 3}};
    int n = sizeof(points)/sizeof(points[0]);
    convexHull(points, n);
    return 0;
}

输出: 

(0,3)
(4,4)
(3,1)
(0,0)

时间复杂度: O(nLogn),其中 n 为输入点的数量。

    第一步(找到最底部的点)需要 O(n) 时间。第二步(对点进行排序)需要 O(nLogn) 时间。第三步需要 O(n) 时间。在第三步中,每个元素最多被推送和弹出一次。因此,第六步逐个处理点需要 O(n) 时间,假设堆栈操作需要 O(1) 时间。总体复杂度为 O(n) + O(nLogn) + O(n) + O(n),即 O(nLogn)。

辅助空间: O(n),因为使用了显式堆栈,所以没有占用额外的空间。

凸包的应用:

    凸包的应用范围很广,包括:

    碰撞检测:凸包可用于快速确定两个物体是否发生碰撞。这在计算机图形学和物理模拟中很有用。

    图像处理:凸包可用于分割图像中的对象。这对于诸如对象识别和跟踪之类的任务很有用。

    计算几何:凸包用于各种计算几何算法,例如查找最近的点对和计算一组点的直径。


http://www.ppmy.cn/devtools/117437.html

相关文章

神奇的可变模板参数的应用(C++标准库双向链表 list 中的emplace函数实现)

我们先来看一个可以构造任意对象的函数&#xff1a; /// <summary> /// 可以构造任意对象的函数 /// </summary> /// <typeparam name"MyClass">要转换对象的类型</typeparam> /// <typeparam name"...MyClassConstructorParameterT…

用Python实现运筹学——Day 3: 线性规划模型构建

一、学习内容 线性规划模型构建的步骤与技巧 线性规划&#xff08;Linear Programming, LP&#xff09;模型构建是运筹学中的核心内容&#xff0c;通常用于求解资源的最优分配问题。要从实际问题中提取出一个线性规划模型&#xff0c;需要按照以下步骤进行&#xff1a; 问题描…

怎么删除右键出现的Open Folder as Intellij IDEA Project

首先&#xff0c;WinR 输入 regedit &#xff08;注册表编辑器&#xff09;&#xff0c;然后按Enter。 1.右键单击文件夹背景时&#xff08;出现的情况&#xff09;&#xff1a; 在注册表中打开“ 计算机 \ HKEY_CLASSES_ROOT \ Directory \ Background \ shell \ IDEA Commun…

逆概率加权(R和Python案例)

逆概率加权&#xff08;Inverse Probability Weighting, IPW&#xff09;是一种统计技术&#xff0c;用于观察性研究中调整混杂变量的影响&#xff0c;以便更准确地估计因果关系。这种方法特别有用于在无法进行随机化实验的情况下&#xff0c;通过给予不同个体不同的权重&#…

Electron-vue asar 局部打包优化处理方案——绕开每次npm run build 超级慢的打包问题

背景 因为组员对于 Electron 打包过程存在比较迷糊的状态&#xff0c;且自己也没主动探索 Electron-vue 打包细节&#xff0c;导致每次打包过程都消耗 5-6 分钟的时间&#xff0c;在需要测试生产打包时&#xff0c;极其浪费时间&#xff0c;为此针对 Electron-vue 打包的几个环…

JVM基本组成

目录 JDK\JRE\JVM 架构 Java执行流程 工作原理 类型 优点 缺陷 JDK\JRE\JVM JDK:全称“Java Development Kit”&#xff0c;Java开发工具包&#xff0c;提供javac编译器jheap、jconsole 等监控工具&#xff1b;JRE:全称“Java Runtime Environment”&#xff0c;Java 运…

MySQL练手题--获得最近第二次的活动(困难)

一、准备工作 Create table If Not Exists UserActivity (username varchar(30), activity varchar(30), startDate date, endDate date); Truncate table UserActivity; insert into UserActivity (username, activity, startDate, endDate) values (Alice, Travel, 2020-02-…

java基础(3)数组的定义与使用

目录 1.前言 2.正文 2.1数组的概念 2.2数组的创建与初始化 2.2.1数组的创建 2.2.1数组的静态初始化 2.2.2数组的动态初始化 2.3数组是引用类型 2.3.1引用类型与基本类型区别 2.3.2认识NULL 2.4二维数组 2.5数组的基本运用 2.5.1数组的遍历 2.5.2数组转字符串 2.…