一组自定义类对象及其<运算符

时间:2019-02-06 14:54:10

标签: c++ set operator-overloading

我正在尝试实现A* algorithm(在Qt中具有可视化功能)。我有这种方法:

result_path astar_algorithm::calculate(mapview* m_view)
{
    map_view = m_view;
    auto closed_set = std::vector<std::shared_ptr<node>>();
    auto start_node = std::make_shared<node>(_start);
    auto open_set = std::vector<std::shared_ptr<node>>{start_node};
    std::map<node, node> came_from;

    std::shared_ptr<node> current;
    while (!open_set.empty())
    {
        current = *std::min_element(open_set.begin(), open_set.end());
        if (*current == _end)
        {
            // TODO: Reconstruct a result path!!!
            break;
        }
        open_set.erase(std::find(open_set.begin(), open_set.end(), current));
        closed_set.push_back(current);
        auto neighbors = get_neighbors(*current);
        for (auto& neighbor : neighbors)
        {
            if (std::find_if(closed_set.begin(), closed_set.end(),
                             [&](std::shared_ptr<node> const& p) { return *p == neighbor; }) !=
                closed_set.end())
                continue;

            auto tentative_g_score = current->G + 1;

            if (std::find_if(open_set.begin(), open_set.end(), [&](std::shared_ptr<node> const& p) {
                    return *p == neighbor;
                }) == open_set.end())
            {
                neighbor.G = tentative_g_score;
                neighbor.H = heuristic_cost_estimate(neighbor.pos, _end);
                neighbor.parent = current;
                open_set.push_back(std::make_shared<node>(neighbor));
            }
            else if (tentative_g_score < neighbor.G)
            {
                neighbor.parent = current;
                neighbor.G = tentative_g_score;
            }
        }
    }
    auto result = result_path();
    while (*current != *start_node)
    {
        result.path.push_back(current->pos);
        current = current->parent;
    }
    result.path.push_back(start_node.pos);
    std::reverse(result.path.begin(), result.path.end());
    return result;
}

它可以工作,但是我有一些问题:

if (std::find_if(closed_set.begin(), closed_set.end(),
                             [&](std::shared_ptr<node> const& p) { return *p == neighbor; }) !=
                closed_set.end())
                continue;

此行检查node中是否存在std::vector,如果存在,它将继续循环(然后有第二行与此类似,它仅检查节点是否实际上不存在在向量中)。我想更好的方法是将这些节点存储在向量中,然后搜索和进一步添加会更容易(因为我只需要检查insert是否成功)。

问题是,要使这项工作有效,我必须实现<运算符。所以我做到了。我还制作了==!=

class node
{
public:
    node() {}
    node(const QPoint& p) : pos(p) {}
    bool operator == (const node& o ) const { return pos == o.pos; }
    bool operator == (const QPoint& o ) const { return pos == o; }
    bool operator != (const node& o) const {return pos != o.pos; }
    bool operator <(const node& o ) const { return G + H < o.G + o.H; }
    QPoint pos;
    std::shared_ptr<node> parent;
    int G = 0;
    int H = 0;
};

它非常适合早期搜索std::min_element(它搜索具有最低F值(F=G+H)的节点,它使用<运算符。但是后来我尝试使用一个集合,所以设置了方法开头的那两个向量,当我只想insert甚至检查某个节点是否已经在集合中,然后insert时我有一个问题。这些nodes的许多值将具有相同的G+H值,因为我使用的迷宫很简单(即完全没有地形的迷宫)。我在调试器下进行了检查,并且具有唯一.pos值(QPoint)的节点没有被添加到集合中,就像它们不是唯一的一样(但是如果节点具有不同的G+H值,而不是集合中的任何节点)。对于矢量,当然可以在相同的节点上工作,因为没有进行检查,我在调试器下仔细检查了所有内容。

我不知道我是否弄错了,但是我认为它会使用==!=运算符,但是如以下答案所示:link,它实际上使用了<运算符,在我的情况下,不会区分两个节点(因为每个节点的唯一部分是其在网格中的位置(节点代表网格中的一个盒子,可以表示类似的迷宫或类似东西))

因此,是否有我做错的事情或实际上是正确的事情,所以插入(检查元素是否唯一)或检查元素是否存在于集合中使用<运算符,而我对此无能为力? (因为我想让我的<运算符与G+H进行比较,然后我希望搜索/插入使用==运算符进行比较)

这是我编写的示例(我忘记了从命令行获得Microsoft C ++编译器-cl.exe

#include <algorithm>
#include <iostream>
#include <memory>
#include <set>

class Point
{
public:
    int _x, _y;
    Point() : _x(0), _y(0) {}
    Point(int x, int y) : _x(x), _y(y) {}
    bool operator==(const Point& p) const { return _x == p._x && _y == p._y; }
    bool operator!=(const Point& p) const { return _x != p._x && _y != p._y; }
};

class node
{
public:
    node() {}
    node(const Point& p) : pos(p) {}
    bool operator==(const node& o) const { return pos == o.pos; }
    bool operator==(const Point& o) const { return pos == o; }
    bool operator!=(const node& o) const { return pos != o.pos; }
    bool operator<(const node& o) const { return G + H < o.G + o.H; }
    Point pos;
    std::shared_ptr<node> parent;
    int G = 0;
    int H = 0;
};

int main()
{
    node n1(Point(0, 0));
    n1.G = 1;
    n1.H = 1;
    node n2(Point(1, 1));
    n2.G = 2;
    n2.H = 2;
    node n3(Point(2, 2));
    n3.G = 1;
    n3.H = 1;
    std::set<node> nodes;
    nodes.insert(n1);
    nodes.insert(n2);
    nodes.insert(n3);
    auto min = (*std::min_element(nodes.begin(), nodes.end())).pos;
    std::cout << min._x << " " << min._y << '\n';
    std::cout << nodes.size() << '\n';
}

>main.exe
0 0
2

std::min_element可以工作,但是对我来说,那是3个唯一的节点(不同的.pos值),因此集合中应该有3个节点。这就是我要实现的目标

1 个答案:

答案 0 :(得分:2)

  

我认为它将使用==!=运算符

否,std::set不使用运算符==!=std::set仅使用一个函数,即比较函数(第二个模板参数,默认为std::less<T>)。

唯一性基于等价关系,该等价关系是通过两次应用相同的比较函数来得出的:!a<b && !b<a

似乎您实际上并不需要唯一性,在这种情况下,您可以使用std::multiset。它将维持顺序,但不会强制唯一性。

std::set<node> nodes;
. . .
auto min = (*std::min_element(nodes.begin(), nodes.end())).pos;

std::min_element始终为O(N)。在set上使用它会破坏拥有set的目的。 只需获得第一个元素,该元素将是最小的(根据比较函数)。

auto min = begin(nodes)->pos;