元素如何在vector中找到自己的索引?

How can an element find its own index in a vector?

本文关键字:自己的 索引 vector 元素      更新时间:2023-10-16

我正在尝试使用std::unique_ptr重新实现树数据结构,其想法是父节点将拥有其子节点,这些子节点存储在unique_ptr的向量中。

由于接口的原因,我需要一个节点销毁自己的方法。在这种情况下,我认为节点应该将自己从父节点的子向量中擦除。

下面的实现"工作"(在c++11编译器中),但是它丑得要命,我确信这是处理这个问题的次优方法。

#include <iostream>
#include <memory>
#include <vector>
#include <algorithm>
struct Node {
    typedef std::vector<std::unique_ptr<Node>> vec_node_uptr;
    unsigned        id;
    Node*           parent;
    vec_node_uptr   child_nodes;
    // ctor
    Node(unsigned id): id(id){ parent = nullptr; }
    void add_child(Node* new_child){
        new_child -> parent = this;
        child_nodes.push_back( std::unique_ptr<Node>(std::move(new_child) ) );
    }
    int where_am_i(){
        int result_ = 0;
        for(auto& i: this -> parent -> child_nodes) {
            if (this == i.get()) {
                return result_;
            } else {
                result_++;
            }
        }
    }
    void suicide(){
        parent -> child_nodes.erase(parent -> child_nodes.begin()+ where_am_i());
    }
};

int main()
{
    std::unique_ptr<Node> root(new Node(0));
    root -> add_child(new Node(1));
    root -> add_child(new Node(2));
    root -> child_nodes[0] -> add_child(new Node(3));
    root -> child_nodes[0] -> add_child(new Node(4));
    root -> child_nodes[1] -> add_child(new Node(5));
    root -> child_nodes[1] -> add_child(new Node(6));
    root -> child_nodes[1] -> suicide();
    return 0;
}

有什么建议吗?也许用std::find ?

您可以使用find_if和lambda:

更优雅地解决这个问题
void suicide() 
{
    auto& parentsChildren = parent->child_nodes;
    parentsChildren.erase(find_if(begin(parentsChildren), end(parentsChildren), 
                           [&](const unique_ptr<Node>& node) { return node.get() == this; }));
}

如果您希望当前数据结构具有恒定时间的where_am_i(),则需要在节点本身中存储索引或迭代器。这是(a)重复的,并且(b)将导致进一步的复杂性,因为每当您删除不是其父节点的最后一个子节点时,您将需要更新所有后续子节点的索引/迭代器…

然而,创建一个恒定时间的where_am_i()可能没有真正的优势,因为从vector中删除一个元素无论如何都是O(n),除非你总是从末尾(或接近末尾)删除。

但是,如果您通常会从末尾删除,并且如果从来没有必要将一组子节点的所有权从父节点转移,那么这里有一个替代的,更简单的设计,避免了在每个节点中存储索引或迭代器的需要:

c++标准保证std::vector像数组一样在内存中连续地布局它们的内容。因此,如果child_nodes向量实际上是按值存储它的元素——即,如果它被声明为

typedef std::vector<Node> vec_node_uptr;
vec_node_uptr child_nodes;

则可以通过简单地从给定元素的地址中减去向量中第一个元素的地址,让指针算法为您进行除法,从而在常数时间内找到位置:

size_t where_am_i() {
    return this - &parent->child_nodes[0];
}