Convex Hull:O(n^2)算法

课程:计算几何
书籍:计算几何:算法与应用

采用一种增量式(Incremental)的思维。考虑在已有的凸包上,在添加一个新的顶点,应该如何操作。
我们可以从新增的点向凸包引射线,我们能找到新增点与凸包的两个切点:s和t,s和t将凸包分成两段。其中有一段上的凸包顶点是需要更新的,另一段上的凸包顶点是需要保留的。具体的怎么判断请大家自行观看上述计算几何课程


代码:
Point.h: 有所更改,为了逆时针记录,类中添加了前后索引。

#pragma once
#ifndef POINT_H
#define POINT_H

#include<iostream>
using namespace std;

class Point {
public:
    int index;
    float x, y;
    bool isExtremePoint;

    int pre, next;


public:
    Point() { index = 0; x = y = 0.0f; isExtremePoint = false; }
    ~Point() {}

    Point(int index, float xx, float yy) {
        this->index = index;
        x = xx; y = yy;
    }

    Point(Point &temp) :index(temp.index), x(temp.x), y(temp.y), isExtremePoint(temp.isExtremePoint){}


    friend ostream& operator<<(ostream& os, Point& temp) {
        os << "Point index: " << temp.index << " (x,y): " << temp.x << "," << temp.y << endl;
        return os;
    }
};

#endif // !POINT_H

Mian.cpp

/*  indere 2018/6/23
*   O(n^2)构造凸包算法是递增式的算法,在已有凸包的基础上,如果需要加入一个新点,应该如何进行删除和增添
*   输入:顶点数以及顶点坐标
*   输出:极点编号(看情况是否按Counter-Clockwise输出)
/

/*
10
7 9
-8 -1
-3 -1
1 4
-3 9
6 -4
7 5
6 6
-6 10
0 8
*/
#include"Point.h"

Point *points;
int pointsize;
Point* CH;
int chsize;

void Initialize();                                              //初始化points数组
void CCW_sort(Point* points);                                   //将顶点数组进行逆时针排序
int  ToLeft(Point v1, Point v2, Point s);                       //ToLeft Test
int  InitialCH();                                               //初始化最初的Convex Hull:逆时针 包含三个不共线的顶点
void ConstructConvexHull(Point* points, Point* CH, int begin);  //构造凸包

int main() {

    Initialize();
    CCW_sort(points);       

    int begin = InitialCH() + 1;
    if (begin < 0)                  //不存在新增点,原顶点组不能组成凸包
        return -1;

    ConstructConvexHull(points, CH, begin);

    cout << "The result :" << endl;
    for (int i = 0; i < chsize; i++) {
        cout << CH[i];
    }

    return 0;
}

void Initialize() {
    cin >> pointsize;
    points = new Point[pointsize];
    for (int i = 0; i < pointsize; i++) {
        float x, y;
        cin >> x >> y;
        points[i] = Point(i + 1, x, y);
    }

    //找出Y轴最小的点,放在数组的第一位。方便后续的逆时针排序
    int k = 0;
    for (int i = 1; i < pointsize; i++) {
        if (points[k].y > points[i].y)
            k = i;
    }
    Point temp = points[0];
    points[0] = points[k];
    points[k] = temp;
}

void CCW_sort(Point* points) {
    //我们采用选择排序

    int k,begin = 0;
    for (int i = 1; i < pointsize; i++) {
        k = i;
        for (int j = i + 1; j < pointsize; j++) {
            int judge = ToLeft(points[begin], points[k], points[j]);        //比较器为ToLeft
            if (judge != 1) {           //不在左侧 
                if (judge == -1) {      //在右侧,更新
                    k = j;  
                }
                else if (judge == 2) {  //共线,如果距离更近,更新
                    float len1 = (points[j].x - points[begin].x) * (points[j].x - points[begin].x) + (points[j].y - points[begin].y) *(points[j].y - points[begin].y);
                    float len2 = (points[i].x - points[begin].x) * (points[i].x - points[begin].x) + (points[i].y - points[begin].y) *(points[i].y - points[begin].y);
                    if (len1 < len2)
                        k = j;
                }
            }
        }

        //swap
        Point temp = points[k];
        points[k] = points[i];
        points[i] = temp;
    }
}

int  ToLeft(Point v1, Point v2, Point s) {
    float result = (v2.x *s.y + v1.x * v2.y + v1.y * s.x)
        - (v1.y * v2.x + v1.x * s.y + v2.y * s.x);

    if (result > 0.0f)      //左侧
        return 1;
    if (result == 0.0f)     //共线
        return 2;
    if (result < 0.0f)      //右侧
        return -1;
}

int InitialCH() {           //将逆时针排序后的Points中先取三个不共线的点,来构造最初的Convex Hull
    CH = new Point[pointsize];
    CH[0] = points[0];
    int begin = 1, next = 2;
    while (ToLeft(points[0], points[begin], points[next]) == 2 && next < pointsize) {   //共线,继续寻找
        begin++;
        next++;
    }
    if (next >= pointsize) {
        cout << "所有顶点均共线,无法组成凸包" << endl;
        return -2;
    }
    CH[1] = points[begin];
    CH[2] = points[next];
    chsize = 3;

    //设置前驱与后继
    CH[0].pre = 2;
    CH[0].next = 1;

    CH[1].pre = 0;
    CH[1].next = 2;

    CH[2].pre = 1;
    CH[2].next = 0;

    return next;
}

void ConstructConvexHull(Point* points, Point* CH, int begin) {

    int t, s;
    bool add_to_ch;
    for (int i = begin; i < pointsize; i++) {
        add_to_ch = false;
        for (int j = 0; j < chsize; j++) {

            int judge1 = ToLeft(points[i], CH[j], CH[CH[j].pre]);
            int judge2 = ToLeft(points[i], CH[j], CH[CH[j].next]);
            if (judge1 == 1 && judge2 == -1)            // L + R
                CH[j].isExtremePoint = false;           //原极点被更新为非极点
            else {
                CH[j].isExtremePoint = true;            //原极点仍然是极点
                add_to_ch = true;

                if (judge1 != 1 && judge2 != 1) //R + R
                    t = j;
                else if (judge1 != -1 && judge2 != -1)  //L + L
                    s = j;
            }
        }
        if (add_to_ch) {
            //增加新点
            for (int i = chsize + 1; i > t + 1; i--) {
                CH[i] = CH[i - 1];
            }
            CH[t + 1] = points[i];
            CH[t + 1].isExtremePoint = true;
            CH[t + 1].pre = t;
            CH[t + 1].next = s;
            chsize++;

            //删除CH中非极点的点
            for (int i = 0; i < chsize; i++) {
                if (!CH[i].isExtremePoint) {
                    CH[CH[i].pre].next = CH[i].next;
                    CH[CH[i].next].pre = CH[i].pre;
                    for (int j = i + 1; j < chsize; j++) {
                        CH[j - 1] = CH[j];
                    }
                    chsize--;
                }
            }
        }
    }
}

这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值