如何将STD :: List中最近的“Point”对象删除为某些x,y?

时间:2009-02-11 20:49:47

标签: c++ list pointers distance

我有一个点类:

class Point {
public:
    int x, y;
    Point(int x1, int y1)
    {
        x = x1;
        y = y1;
    }
};

和点列表:

std::list <Point> pointList;
std::list <Point>::iterator iter;

我正在向我的pointList推送积分(尽管如果还没有推出那么列表可能还没有积分)。

我有两个问题:

如何从列表中删除最接近某个(x,y)的点?

假设我有x,y(5,12),我想找到最接近该点的列表中的Point并将其从STD :: List中删除。

我知道我将不得不使用距离公式,我将不得不使用迭代器遍历列表,但是我在描述如何跟踪哪个点最接近迭代时遇到了一些麻烦通过清单。

如何返回给定(x,y)x半径内的数组或点列表?

与上一个问题类似,但我需要一个指向给定(x,y)半径范围内“点”对象的指针列表。另外,我应该返回一个数组还是一个List?

如果有人能帮助我,我仍然在努力克服C ++,我很感激。

5 个答案:

答案 0 :(得分:6)

使用std :: list :: iterator变量跟踪循环列表中的最近点。当你到达列表的末尾时,它将包含最近的点,并可用于擦除项目。

void erase_closest_point(const list<Point>& pointList, const Point& point)
{
    if (!pointList.empty())
    {
        list<Point>::iterator closestPoint = pointList.begin();
        float closestDistance = sqrt(pow(point.x - closestPoint->x, 2) +
                                     pow(point.y - closestPoint->y, 2));

        // for each point in the list
        for (list<Point>::iterator it = closestPoint + 1;
             it != pointList.end(); ++it)
        {
            const float distance = sqrt(pow(point.x - it->x, 2) +
                                        pow(point.y - it->y, 2));

            // is the point closer than the previous best?
            if (distance < closestDistance)
            {
                // replace it as the new best
                closestPoint = it;
                closestDistance = distance
            }
        }

        pointList.erase(closestPoint);
    }
}

在给定点的半径范围内构建点列表是类似的。请注意,空半径列表通过引用传递给函数。通过引用将点添加到列表将消除在按值返回向量时复制所有点的需要。

void find_points_within_radius(vector<Point>& radiusListOutput,
                               const list<Point>& pointList,
                               const Point& center, float radius)
{
    // for each point in the list
    for (list<Point>::iterator it = pointList.begin();
         it != pointList.end(); ++it)
    {
        const float distance = sqrt(pow(center.x - it->x, 2) +
                                    pow(center.y - it->y, 2));

        // if the distance from the point is within the radius
        if (distance > radius)
        {
            // add the point to the new list
            radiusListOutput.push_back(*it);
        }
    }
}

再次使用copy if:

struct RadiusChecker {
    RadiusChecker(const Point& center, float radius)
        : center_(center), radius_(radius) {}

    bool operator()(const Point& p)
    {
        const float distance = sqrt(pow(center_.x - p.x, 2) +
                                    pow(center_.y - p.y, 2));
        return distance < radius_;
    }

private:
    const Point& center_;
    float radius_;
};

void find_points_within_radius(vector<Point>& radiusListOutput,
                               const list<Point>& pointList,
                               const Point& center, float radius)
{
    radiusListOutput.reserve(pointList.size());
    remove_copy_if(pointList.begin(), pointList.end(),
                   radiusListOutput.begin(),
                   RadiusChecker(center, radius));
}

请注意,如果您需要额外的性能,可以删除 sqrt ,因为幅度的平方对于这些比较同样适用。此外,如果您真的想要提高性能而不是考虑允许像quadtree这样的场景分区的数据结构。第一个问题与collision detection密切相关,并且有大量关于该主题的有价值信息。

答案 1 :(得分:3)

你应该如何制作。只需遍历列表中的所有项目并跟踪已找到的最小距离,以及在两个变量中找到的最近点,如果问题出现,请确保您不会将该点与自身匹配。然后删除你找到的那个点。

如何将其完全保留为练习。

如果要从另一个点获取给定半径内的点列表,请迭代列表并构建仅包含指定范围内的点的第二个列表。

同样,如何在代码中制作它作为练习留给你。

答案 2 :(得分:3)

您可以使用STL和Boost.Iterators以及Boost.Bind的组合来执行此操作 - 为了您的方便,我在这里粘贴解决方案的全部来源:

#include <list>
#include <cmath>
#include <boost/iterator/transform_iterator.hpp>
#include <boost/bind.hpp>
#include <cassert>

using namespace std;
using namespace boost;

struct Point {
    int x, y;
    Point() : x(0), y(0) {}
    Point(int x1, int y1) : x(x1), y(y1) {}
    Point(Point const & other) : x(other.x), y(other.y) {}
    Point & operator=(Point rhs) { rhs.swap(*this); return *this; }
    void swap(Point & other) { std::swap(other.x, x); std::swap(other.y, y); }
};

double point_distance(Point const & first, Point const & second) {
    double x1 = first.x;
    double x2 = second.x;
    double y1 = first.y;
    double y2 = second.y;
    return sqrt( ((x2 - x1) * (x2 -x1)) + ((y2 - y1) * (y2 - y1)) );
}

int main(int argc, char * argv[]) {
    list<Point> points;
    points.push_back(Point(1, 1));
    points.push_back(Point(2, 2));
    points.push_back(Point(3, 3));
    Point source(0, 0);
    list<Point>::const_iterator closest = 
        min_element(
                make_transform_iterator(
                    points.begin(),
                    bind(point_distance, source, _1)
                    ),
                make_transform_iterator(
                    points.end(),
                    bind(point_distance, source, _1)
                    )
                ).base();
    assert(closest == points.begin());
    return 0;
}

解决方案的核心是使用变换迭代器使用point_distance函数转换列表中的每个元素,然后从所有距离获得最小距离。您可以在遍历列表时执行此操作,最后到达transform_iterator以获取基本迭代器(使用base()成员函数)。

现在您已拥有该迭代器,您可以将assert(closest == points.begin())替换为points.erase(closest)

答案 3 :(得分:0)

你必须保持迭代器以后删除它。

std::list<Point>::iterator closest;
std::list<Point>::iterator it = pointList.begin();
double min_dist=dist(your_point, *it);
++it;
for (; it != pointList.end(); ++it)
{
        double actual_dist = dist(your_point, *it);
        if (actual_dist < min_dist)
        {
                min_dist = actual_dist;
                closest = it;
        }
}

pointList.erase(closest);

答案 4 :(得分:0)

我同意之前的解决方案,只想添加另一个想法。虽然您的Point类不是很大,因此副本不是真正的问题,您可以考虑使用Point *作为列表。这样,当您创建第二个列表时,您将指针存储到同一个类。如果您从多个列表中删除而没有管理所有已创建点的“主”,那么如果您没有删除基础类或意外删除仍然存在的类,则可能会创建内存泄漏被用在另一个列表中。但是,需要考虑的事情取决于系统的发展方式。