首页 > 编程语言 >图算法太难懂?凸包算法搞不通?看这篇文章就够了

图算法太难懂?凸包算法搞不通?看这篇文章就够了

时间:2024-09-03 14:20:52浏览次数:7  
标签:Point int hull 就够 凸包 算法 points

标题:你以为凸包算法只是数学游戏?不,这才是竞赛中的制胜法宝!

你以为几何算法只是竞赛中的小儿科,顶多画个漂亮图形?但是,朋友,你要知道,如果你还停留在这样的认知,那你已经out了!凸包(Convex Hull)——听起来像个不起眼的小问题,但实际上,它是算法竞赛中的核武器,是能让你在众多参赛者中脱颖而出的绝技!很多人觉得这个算法不过就是点和线的简单组合,真正复杂的东西不在这里。但真的是如此吗?其实,凸包算法不仅是计算几何的基础,也是深入理解算法优化和空间复杂度的绝佳案例。今天,我们就要来掰开揉碎了,深入浅出地揭开这些经典算法的神秘面纱!

什么是凸包?一听名字就知道不简单!

在二维平面上,给你一堆点,你能画出一个“包”裹住这些点的最小凸多边形吗?这就是所谓的凸包问题。你或许以为这没什么大不了的,但试想一下,应用场景遍布图像处理、机器人路径规划、数据分析中的聚类问题等等,简直就是无所不在!你在算法竞赛中遇到的复杂度都离不开这几个字:凸包。

那么问题来了,如何高效地找到这个“包”?在计算几何的世界里,有几种经典的算法——Graham 扫描法(Graham Scan)Jarvis March(礼花算法)Andrew’s Monotone Chain(单调链),每一个名字听起来都那么高大上,但今天我们只挑最具代表性的两种——Graham 扫描法和礼花算法,细细道来,帮你轻松攻克!

1. Graham 扫描法:每一个步骤都堪称艺术品的算法

Graham 扫描法(Graham Scan),一个听起来像科学家的名字,其实是算法中的“艺术家”。它将凸包问题转换成了一个排序问题——用时间复杂度 (O(n \log n)) 的复杂度来搞定一切。这就是聪明人的做法,聪明在哪里?就在于,它利用了极角排序和栈结构的妙用,让你在构建凸包的过程中不断找到“最优解”。

步骤分析:一步一步走上“巅峰”
  1. 找到最低且最左的点(起点)
    你以为随便找个点就行?不!最低且最左的点才是你成功的基石。这个点一定在凸包上,不信?数学定理摆在那里,绝对错不了!

  2. 按极角排序:给点们排排队,按顺序来
    把剩下的点按照与起点的极角从小到大排序。这里要注意了,排序可是精髓中的精髓!极角排序是这个算法的灵魂,搞定这个,你就成功了一半。

  3. 遍历点集构建凸包:要有耐心,急不得!
    开始用栈来维护我们的凸包顶点,遇到“向左转”的点就压入栈中,遇到“向右转”的点就弹出栈顶。搞不清楚左右?没关系,叉积公式给你撑腰!这个过程就像是在锻造一块完美的宝石,耐心打磨,成品就是惊艳的。

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

typedef struct {
    double x, y;
} Point;

// 计算两点之间的极角(atan2)
double polar_angle(Point p0, Point p1) {
    return atan2(p1.y - p0.y, p1.x - p0.x);
}

// 计算两向量的叉积
double cross_product(Point a, Point b, Point c) {
    return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
}

// 比较函数,用于按极角排序点
int compare_points(const void *a, const void *b, void *origin) {
    Point *p0 = (Point *)origin;
    Point *p1 = (Point *)a;
    Point *p2 = (Point *)b;
    double angle1 = polar_angle(*p0, *p1);
    double angle2 = polar_angle(*p0, *p2);

    if (angle1 < angle2) return -1;
    if (angle1 > angle2) return 1;

    double dist1 = (p1->x - p0->x) * (p1->x - p0->x) + (p1->y - p0->y) * (p1->y - p0->y);
    double dist2 = (p2->x - p0->x) * (p2->x - p0->x) + (p2->y - p0->y) * (p2->y - p0->y);

    return (dist1 < dist2) ? -1 : 1;
}

// 求解凸包的Graham扫描算法
void graham_scan(Point points[], int n, Point hull[], int *hull_size) {
    // 找到最低且最左的点作为起点
    int min_index = 0;
    for (int i = 1; i < n; i++) {
        if (points[i].y < points[min_index].y || 
            (points[i].y == points[min_index].y && points[i].x < points[min_index].x)) {
            min_index = i;
        }
    }

    Point temp = points[0];
    points[0] = points[min_index];
    points[min_index] = temp;

    qsort_r(points + 1, n - 1, sizeof(Point), compare_points, &points[0]);

    int top = 1;
    hull[0] = points[0];
    hull[1] = points[1];

    for (int i = 2; i < n; i++) {
        while (top > 0 && cross_product(hull[top - 1], hull[top], points[i]) <= 0) {
            top--;
        }
        hull[++top] = points[i];
    }

    *hull_size = top + 1;
}

// 测试用例
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]);
    Point hull[n];
    int hull_size;

    graham_scan(points, n, hull, &hull_size);

    printf("凸包点:\n");
    for (int i = 0; i < hull_size; i++) {
        printf("(%.1f, %.1f)\n", hull[i].x, hull[i].y);
    }

    return 0;
}

看完代码,是不是有点豁然开朗的感觉?这就是Graham扫描法的魅力——简洁而不简单,强大又优雅。

2. Jarvis March(礼花算法):简单但不平凡,天生的贪心策略

如果你以为算法世界里只要用上高级排序就能高枕无忧,那你就错了!Jarvis March,俗称“礼花算法”,是一种基于贪心思想的凸包算法。简单?可不要小看它!虽然时间复杂度为 (O(nh)),看起来效率不高,但在某些场景下却能出奇制胜!为什么?因为它的实现相当直观——从最左下角的点开始,每次都选择下一个能形成“最左转”的点,直到回到起始点。

代码实现:老少咸宜,通俗易懂
#include <stdio.h>

#define MAX_POINTS 100

typedef struct {
    double x, y;
} Point;

int orientation(Point p, Point q, 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;
}

void jarvis_march(Point points[], int n) {
    if (n < 3) return;

    int hull[MAX_POINTS];
    int hull_size = 0;

    int l = 0;
    for (int i = 1; i < n; i++)
        if (points[i].x < points[l].x)
            l = i;

    int p = l, q;
    do {
        hull[hull_size++] = p;
        q = (p + 1) % n;

        for (int i = 0; i < n; i++) {
           
            if (orientation(points[p], points[i], points[q]) == 2) {
                q = i;
            }
        }

        p = q;
    } while (p != l);

    printf("凸包点:\n");
    for (int i = 0; i < hull_size; i++) {
        printf("(%.1f, %.1f)\n", points[hull[i]].x, points[hull[i]].y);
    }
}

// 测试用例
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]);

    jarvis_march(points, n);

    return 0;
}

看,这就是贪心策略的直接应用!代码简单易懂,却不失优雅。对于需要快速入门的同学来说,Jarvis March 是一个非常好的起步选择。

总结:凸包算法远不止于“点与线”的游戏

很多人以为几何算法仅仅是个“点与线”的游戏,但真正的高手都知道,算法的每一步都有其数学和编程的深意。从 Graham 扫描法的极角排序到 Jarvis March 的贪心选择,每一种方法都是从问题的本质出发,探索更高效的解法。掌握这些算法,你不仅能轻松应对各种竞赛题目,还能在面试中赢得面试官的青睐!所以,不要再认为几何算法只是学术游戏了,它们可是你打开算法之门的钥匙!

还不赶快动手试试?今天的分享就到这里,我们下次见!

标签:Point,int,hull,就够,凸包,算法,points
From: https://blog.csdn.net/qq_36916968/article/details/141860473

相关文章

  • 求职季来了,是时候让豆包MarsCode 陪你刷算法题了
    金九银十求职季,对于广大技术岗求职者来说,拥有扎实的算法知识是打开理想职业大门的金钥匙。为了更好地帮助广大求职者找到自己心仪的岗位,豆包MarsCode特推出代码练习能力,将全功能的代码编辑器和AI能力相结合,希望帮助开发者更快速地在求职季进行算法题目练习,100道大厂真题,助你高效......
  • 卡尔曼滤波算法的学习总结
    本文为作者学习卡尔曼滤波算法后的学习总结,如有错误请指正,万分感谢!前言本文学自B站up主华南小虎队,原视频讲得很好,推荐去观看。原视频卡尔曼滤波讲解一、简介(1)作用在学习卡尔曼滤波之前,我们首先要明白在使用该滤波器后,可以给我们带来什么好处?在此给读者举出一个例子,方......
  • 【数据结构与算法】:十大经典排序算法
    文章目录前言一、冒泡排序(BubbleSort)1.1冒泡排序原理1.2冒泡排序代码1.3输出结果二、选择排序(SelectionSort)2.1选择排序原理2.2选择排序代码2.3输出结果三、插入排序(InsertionSort)3.1插入排序原理3.2插入排序代码3.3输出结果四、希尔排序4.1希尔排序原......
  • 基于SIR模型的疫情发展趋势预测算法matlab仿真
    1.程序功能描述基于SIR模型的疫情发展趋势预测算法.对病例增长进行SIR模型拟合分析,并采用模型参数拟合结果对疫情防控力度进行比较。整体思路为采用SIR微分方程模型,对疫情发展进行过程进行拟合。2.测试软件版本以及运行结果展示MATLAB2022a版本运行3.核心程序Opt.LargeScale......
  • 常见算法和lambda的使用
    常见的七种查找算法:数据结构是数据存储的方式,算法是数据计算的方式。所以在开发中,算法和数据结构息息相关。今天的讲义中会涉及部分数据结构的专业名词,如果各位铁粉有疑惑,可以先看一下哥们后面录制的数据结构,再回头看算法。1.基本查找也叫做顺序查找说明:顺序查找适合于存储结......
  • LLM大模型学习:重磅首发!大模型LLM学习路线图来了!非常详细收藏我这一篇就够了
    ChatGPT的出现在全球掀起了AI大模型的浪潮,2023年可以被称为AI元年,AI大模型以一种野蛮的方式,闯入你我的生活之中。从问答对话到辅助编程,从图画解析到自主创作,AI所展现出来的能力,超出了多数人的预料,让不少人惊呼:“未来是属于AI的”。AI大模型——成为互联网从业者必备技能。......
  • 图的广度优先搜索(BFS)算法与邻接矩阵表示
    图的广度优先搜索(BFS)算法与邻接矩阵表示1.图的表示2.广度优先搜索(BFS)BFS算法步骤:3.使用邻接矩阵的BFS实现4.运行时间分析时间复杂度:空间复杂度:5.BFS使用邻接列表与邻接矩阵的比较BFS在邻接列表上的运行时间:6.结论在计算机科学中,图......
  • 基于SIR模型的疫情发展趋势预测算法matlab仿真
    1.程序功能描述基于SIR模型的疫情发展趋势预测算法.对病例增长进行SIR模型拟合分析,并采用模型参数拟合结果对疫情防控力度进行比较。整体思路为采用SIR微分方程模型,对疫情发展进行过程进行拟合。2.测试软件版本以及运行结果展示MATLAB2022a版本运行 3.核心程序Opt=o......
  • 基于LK光流提取算法的图像序列晃动程度计算matlab仿真
    1.算法运行效果图预览(完整程序运行后无水印) 2.算法运行软件版本matlab2022a 3.部分核心程序(完整版代码包含详细中文注释和操作步骤视频) %晃动指标axes(handles.axes1);imshow(uint8(I0{kk}));axes(handles.axes2);quiver(x,y,hor,ve......
  • 算法与数据结构——二叉树数组表示
    二叉树数组表示在链表表示下,二叉树的存储单元为节点TreeNode,节点之间通过指针相连接。同前面的队列或栈,二叉树同样可以使用数组来表示。表示完美二叉树给定一棵完美二叉树,我们将所有节点按照层序遍历的顺序存储在一个数组中,则每个节点都对应唯一的数组索引。按照层序遍历的特......