Выпуклая оболочка алгоритм Грехема



Дано множество точек
Необходимо найти минимальный многоугольник, содержащий все эти точки

    Идея алгоритма Грехема заключается в последовательном отборе точек, упорядочённых ранее по полярному углу относительно какой-либо точки, которая обязательно будет входить в оболочку. Для этого можем использовать самую нижнюю из самых левых точек.
    Если посмотреть на полученный упорядочённый набор точек, то становится очевидно, что все точки, входящие в оболочку, будут распологаться в этом наборе в той же последовательности, в какой они образуют оболочку. Следовательно, мы можем последовательно их добавлять по одной из набора, но если получится, что в текущей оболочке три последовательные точки образуют угол больше либо равный 180 градусов (ориентированная площадь треугольника меньше либо равна нулю), то необходимо удалить предыдущую точку.
    Случай, когда две последовательные точки в оболочке лежат на одной прямой, можно не рассматривать отдельно, если в сортировке учесть, что чем дальше точка, тем она "приоритетнее".

Сложность O(nlogn).


Листинг C++

point first;
class less_of_ccw
{
public :
    bool operator () (point a, point b)
    {
        if (a.i == first.i) return true;
        if (b.i == first.i) return false;
        if (ccw (first, a, b)) return true;
        if (ccw (first, b, a)) return false;
        return dist (first, a) > dist (first, b);
    }
};
void hull_graham (vector < point > p, vector < int > &ip)
{
    int n = p.size();
    int i;
    for (i = 0; i < n; ++ i)
        p[i].i = i;
    // ищем самую нижнюю из самых левых точек - первая точка
    first = p[0];
    for (i = 1; i < n; ++ i)
        if (first.x > p[i].x || (first.x == p[i].x && first.y > p[i].y))
            first = p[i];
    
    // сортируем точки по по угла относительно первой точки
    sort (p.begin (), p.end (), less_of_ccw ());
    // первая точка оболочки
    ip.push_back (0);
    // ищем вторую точку оболочки
    for (i = 1; i < n && abs (area_triangle (p[0], p[1], p[i])) <= eps; ++ i);
    ip.push_back (1);

    // последовательно добавляем точки в оболочку
    int top = 1; // индекс последней точки в оболочке
    while (i < n)
    {
        // если угол больше pi то извлекаем последнюю точку из оболочки
        if (! ccw (p[ip[top - 1]], p[ip[top]], p[i]))
        {
            -- top;
            ip.pop_back ();
        }
        // иначе добавляем точку в оболочку
        else
        {
            ++ top;
            ip.push_back (i);
            ++ i;
        }
    }
    for (i = 0; i < ip.size(); ++ i)
        ip[i] = p[ip[i]].i;
}

19.06.2007, 19:41


По всем вопросам обращаться: rumterg@gmail.com