Graham扫描凸包算法

news2025/1/13 9:41:39

凸包(Convex Hull)是包含给定点集合的最小凸多边形。凸包算法有多种实现方法,其中包括基于递增极角排序、Graham扫描、Jarvis步进法等。下面,我将提供一个简单的凸包算法实现,基于Graham扫描算法。

Graham扫描算法是一种用于求解平面点集的凸包问题的常见算法。凸包是包含给定点集合的最小凸多边形。Graham扫描算法的基本思想是通过选择一个特殊的起点,将点集按照极角排序,然后通过栈的操作来逐步构建凸包。

以下是Graham扫描算法的基本步骤:

  • 选择极点: 从给定的点集中选择一个极点作为起始点。通常选择最下面且最左边的点,以确保算法的稳定性。

  • 极角排序: 将其他所有点按照相对于极点的极角进行排序。极角可以使用反正切函数(atan2)计算。排序后的点集顺序将确定扫描过程中点的访问顺序。

  • 扫描过程: 从第三个点开始,按照排序后的顺序逐个处理每个点。对于每个点,检查它与栈顶两个点的转向关系(顺时针、逆时针或平行)。如果是逆时针,将该点压入栈;如果是顺时针或平行,则出栈,直到找到逆时针为止。这确保了最终栈中的点构成凸包。

  • 构建凸包: 扫描完成后,栈中的点就是凸包的顶点,它们按照逆时针方向排列。

Graham扫描算法的时间复杂度主要取决于对点的排序操作,通常为 O ( n log ⁡ n ) O(n\log n) O(nlogn),其中n是点的数量。该算法的优势在于其相对简单的实现和较好的性能。然而,需要注意的是,在特定情况下,例如存在大量共线点的情况下,算法的性能可能会有所下降。

import matplotlib.pyplot as plt
import math

# 定义二维点的类
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

# 计算极角
def polar_angle(p0, p):
    dx = p.x - p0.x
    dy = p.y - p0.y
    return math.atan2(dy, dx)

# 判断三个点的转向关系
def orientation(p, q, r):
    val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y)
    if val == 0:
        return 0  # 平行
    return 1 if val > 0 else 2  # 顺时针或逆时针

# Graham扫描算法
def convex_hull(points):
    n = len(points)
    if n < 3:
        print("Convex Hull requires at least 3 points.")
        return []

    # 寻找最下面且最左边的点作为极点
    pivot = min(range(n), key=lambda i: (points[i].y, points[i].x))
    points[0], points[pivot] = points[pivot], points[0]

    # 根据极角对其余点进行排序
    points[1:] = sorted(points[1:], key=lambda p: (polar_angle(points[0], p), p.x, p.y))

    # 构建凸包
    hull = [points[0], points[1]]

    for i in range(2, n):
        while len(hull) > 1 and orientation(hull[-2], hull[-1], points[i]) != 2:
            hull.pop()
        hull.append(points[i])

    return hull

# 示例点集
points = [Point(0, 3), Point(1, 1), Point(2, 2), Point(4, 4), Point(0, 0), Point(1, 2), Point(3, 1), Point(3, 3)]

# 计算凸包
convex_hull_points = convex_hull(points)

# 绘制原始离散点
x_values = [point.x for point in points]
y_values = [point.y for point in points]
plt.scatter(x_values, y_values, color='blue', label='Original Points')

# 绘制凸包
hull_x = [point.x for point in convex_hull_points]
hull_y = [point.y for point in convex_hull_points]
hull_x.append(convex_hull_points[0].x)  # 闭合凸包
hull_y.append(convex_hull_points[0].y)
plt.plot(hull_x, hull_y, color='red', linestyle='-', linewidth=2, label='Convex Hull')

# 显示图例和图形
plt.legend()
plt.xlabel('X')
plt.ylabel('Y')
plt.title('Convex Hull')
plt.grid(True)
plt.show()

在这里插入图片描述

#include <iostream>
#include <vector>
#include <algorithm>
#include <stack>

// 定义二维点的结构体
struct Point {
    double x, y;

    // 构造函数
    Point(double _x, double _y) : x(_x), y(_y) {}

    // 用于排序的比较函数
    static bool compare(const Point& a, const Point& b) {
        return (a.y < b.y) || (a.y == b.y && a.x < b.x);
    }
};

// 计算极角
double polarAngle(const Point& p0, const Point& p) {
    double dx = p.x - p0.x;
    double dy = p.y - p0.y;
    return atan2(dy, dx);
}

// 判断三个点的转向关系
int orientation(const Point& p, const Point& q, const Point& r) {
    double val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
    if (val == 0) return 0;  // 平行
    return (val > 0) ? 1 : 2; // 顺时针或逆时针
}

// Graham扫描算法
std::vector<Point> convexHull(std::vector<Point>& points) {
    size_t n = points.size();
    if (n < 3) {
        std::cerr << "Convex Hull requires at least 3 points." << std::endl;
        return std::vector<Point>();
    }

    // 寻找最下面且最左边的点作为极点
    size_t pivot = 0;
    for (size_t i = 1; i < n; i++) {
        if (points[i].y < points[pivot].y || (points[i].y == points[pivot].y && points[i].x < points[pivot].x)) {
            pivot = i;
        }
    }

    // 将极点移到数组的第一个位置
    std::swap(points[0], points[pivot]);

    // 根据极角对其余点进行排序
    std::sort(points.begin() + 1, points.end(), [&points](const Point& a, const Point& b) {
        double angleA = polarAngle(points[0], a);
        double angleB = polarAngle(points[0], b);

        return (angleA < angleB) || (angleA == angleB && (a.x < b.x || (a.x == b.x && a.y < b.y)));
        });

    // 构建凸包
    std::vector<Point> hull;
    hull.push_back(points[0]);
    hull.push_back(points[1]);

    for (size_t i = 2; i < n; i++) {
        while (hull.size() > 1 && orientation(hull[hull.size() - 2], hull[hull.size() - 1], points[i]) != 2) {
            hull.pop_back();
        }
        hull.push_back(points[i]);
    }

    return hull;
}

int main() {
    // 示例点集
    std::vector<Point> points = { {0, 3}, {1, 1}, {2, 2}, {4, 4}, {0, 0}, {1, 2}, {3, 1}, {3, 3} };

    // 计算凸包
    std::vector<Point> convexHullPoints = convexHull(points);

    // 输出凸包的点
    std::cout << "Convex Hull Points:" << std::endl;
    for (const auto& point : convexHullPoints) {
        std::cout << "(" << point.x << ", " << point.y << ")" << std::endl;
    }

    return 0;
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1374526.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

关于PhpStorm的安装激活与汉化

访问官网下载PhpStorm https://www.jetbrains.com/phpstorm/download/#sectionwindows 点击download 下载好后&#xff0c;双击exe安装程序 点击下一步 选择安装位置 前两个肯定需要勾选&#xff1a; 创建桌面快捷方式&#xff1b;创建关联php&#xff1b; 根据以往经验&am…

CES 2024丨引领变革,美格智能为智能终端带来生成式AI能力

作为电子行业的“风向标”&#xff0c;CES 2024&#xff08;国际消费电子展&#xff09;于1月9日至12日在美国拉斯维加斯举办。本届展会可谓是AI的盛宴&#xff0c;芯片、AI PC、智能家居、汽车科技、消费电子等领域与AI相关的前沿成果接连发布&#xff0c;引领人工智能领域的科…

【数据库】视图索引执行计划多表查询笔试题

文章目录 一、视图1.1 概念1.2 视图与数据表的区别1.3 优点1.4 语法1.5 实例 二、索引2.1 什么是索引2.2.为什么要使用索引2.3 优缺点2.4 何时不使用索引2.5 索引何时失效2.6 索引分类2.6.1.普通索引2.6.2.唯一索引2.6.3.主键索引2.6.4.组合索引2.6.5.全文索引 三、执行计划3.1…

leaflet学习笔记-缓冲区绘制(六)

前言 在GIS开发中&#xff0c;缓冲区的绘制和使用是非常广泛的&#xff0c;一般情况下就是对缓冲区范围内的要素做分析使用&#xff0c;也会有一些其他的操作&#xff0c;下面我就记录一下使用leafletturf.js完成缓冲区的绘制操作 turf.js简介 Turf.js 是一个用于地理空间计…

上架苹果APP的时候在哪里填写APP的隐私政策信息

在如今高度重视数据隐私的时代&#xff0c;开发并上架一个iOS APP时提供透明的隐私政策是非常重要的。苹果公司对此有严格的规定&#xff0c;任何上架至App Store的应用都必须包含一个隐私政策。以下是您在上架苹果APP时填写隐私政策信息的详细步骤和必须注意的事项。 准备隐私…

考古学家 - 华为OD统一考试

OD统一考试 分值&#xff1a; 200分 题解&#xff1a; Java / Python / C 题目描述 有一个考古学家发现一个石碑&#xff0c;但是很可惜发现时其已经断成多段。 原地发现N个断口整齐的石碑碎片&#xff0c;为了破解石碑内容&#xff0c;考古学家希望有程序能帮忙计算复原后的石…

最新PyCharm安装详细教程及pycharm配置_pycharm安装教程

目录 一、PyCharm简介及其下载网站 二、单击网站的Downloads&#xff0c;进入二级页面&#xff0c;选择对应的操作系统下载PyCharm 三、PyCharm的安装程序的安装及其配置(configuration) 1、运行PyCharm Setup 2、安装位置设置 3、安装选项设置 4、开始菜单中PyCharm快捷方式的…

Unity中向量的点乘、叉乘区别和作用以及经典案例

文章目录 点乘&#xff08;Dot Product&#xff09;叉乘&#xff08;Cross Product&#xff09;向量归一化&#xff08;Normalize&#xff09;其他作用 unity开发中我们要计算角度&#xff0c;判断位置&#xff0c;常用点乘、叉乘、归一化等等&#xff0c;我们看看他们的使用案…

Android 通知简介

Android 通知简介 1. 基本通知 图1: 基本通知详情 小图标 : 必须提供,通过 setSmallIcon( ) 进行设置.应用名称 : 由系统提供.时间戳 : 由系统提供,也可隐藏时间.大图标(可选) : 可选内容(通常仅用于联系人照片,请勿将其用于应用图标),通过setLargeIcon( ) 进行设置.标题 : 可选…

中国社科院大学与美国杜兰大学金融管理硕士项目——熬过寒冬,春日暖阳已不远

在金融领域&#xff0c;寒冬似乎成了无法避免的阶段。然而&#xff0c;对于那些坚守岗位的金融从业者来说&#xff0c;熬过寒冬并非无望。正如冬去春来&#xff0c;只要我们采取明智的策略&#xff0c;迈出坚定的步伐&#xff0c;春日的暖阳已在不远方照耀。社科院与美国杜兰大…

SpringBoot+Hutool实现图片验证码

图片验证码在注册、登录、交易、交互等各类场景中都发挥着巨大作用&#xff0c;能够防止操作者利用机器进行暴力破解、恶意注册、滥用服务、批量化操作和自动发布等行为。 创建一个实体类封装&#xff0c;给前端返回的验证码数据&#xff1a; Data public class ValidateCodeV…

JS栈和堆:数据是如何存储的

JS栈和堆&#xff1a;数据是如何存储的 背景JavaScript 是什么类型的语言JavaScript 的数据类型内存空间栈空间和堆空间再谈闭包 背景 JS有多种数据类型&#xff1a;数字型&#xff0c;字符串型&#xff0c;数组型等&#xff0c;虽然 JavaScript 并不需要直接去管理内存&#…

都2024年了,FP卖家还不知道AB站怎么玩?

自从开始写FP独立站各种运营技巧和黑科技的文章&#xff0c;经常都会有朋友来私V&#xff0c;询问怎么进行AB站跳转。 可能是现在平台对于FP商家的限制越来越多&#xff0c;再加上如今到处都是“内卷”的电商环境&#xff0c;让FP商家生存越来越艰难&#xff0c;今天就着重讲一…

美当局批准现货比特币ETF,BTC不涨反跌?解读22页官方文件,SEC的担忧被完全解决了吗?

美东时间2024年1月10日下午&#xff0c;美SEC官宣批准现货比特币ETF的上市和交易&#xff0c;这是一个里程碑时刻&#xff0c;代表着加密资产类别获得主流采用的最重要一步。 11只获得批准的现货比特币ETF分别来自&#xff1a;BlackRock、Bitwise、Grayscale、Hashdex、Valkyri…

CAN201 计网大题收集

网络性能计算 e.g1 e.g2 木桶效应 e.g3 吞吐量 e.g4 时延 e.g5 时延 e.g5 e.g e.g6 拓展 e.g7 传输层 TCP D 拥塞控制算法 拥塞控制算法_哔哩哔哩_bilibili 慢开始&#xff0c;拥塞避免&#xff0c;快重传&#xff0c;快恢复 物理层 根据我印象好像不太需要学物理层这块…

科技成果鉴定测试的意义在哪?鉴定测试报告重要吗?

科技成果鉴定测试是指通过一系列的评估和验证过程&#xff0c;对科技成果所包含的技术特征、技术优势以及市场应用前景进行客观、准确的评估和证明的过程。科技成果鉴定测试可以对科技项目进行全面、系统的评估&#xff0c;从而找出项目的优势和不足之处&#xff0c;并为项目的…

usb静电防护芯片选择

方案1 USBLC6-2SC6 优缺点 优点&#xff1a;进出使用不同的焊盘&#xff0c;如果没有焊接好信号必定不能通过。有效的避免了虚焊导致故障。 缺点&#xff1a;不能省略&#xff0c;调试时也不能省略。 原理图 参考价格 参考来源 USB切换方案&#xff0c;多电脑共用USB方案…

零基础小白如何自学sql?

学习SQL对于数据分析和处理来说非常重要。SQL是一种强大的工具&#xff0c;可以帮助你与数据库沟通&#xff0c;提取&#xff0c;整理和理解数据。 以下是一些学习SQL的建议&#xff1a; 01 前期&#xff1a;SQL数据库学习 了解SQL的基本概念&#xff1a;首先&#xff0c;你…

求幸存数之和 - 华为OD统一考试

OD统一考试(C卷) 分值: 100分 题解: Java / Python / C++ 题目描述 给一个正整数列nums,一个跳数jump,及幸存数量left。运算过程为:从索引为0的位置开始向后跳,中间跳过 J 个数字,命中索引为 J+1 的数字,该数被敲出,并从该点起跳,以此类推,直到幸存left个数为止。…

Java高级工程师20道面试题、答案及案例

文章目录 Java高级工程师面试题、答案及案例&#xff1a; 问题&#xff1a; 在Java中&#xff0c;如何实现线程安全的单例模式&#xff1f;请写出双重检查锁定&#xff08;Double-Checked Locking&#xff09;的实现方式。 答案与案例&#xff1a; public class Singleton {pri…