【问题标题】:C++: need indexed setC++:需要索引集
【发布时间】:2024-01-12 22:36:01
【问题描述】:

我需要一个操作如下的索引关联容器:

  • 最初为空,大小=0。

  • 当我向它添加一个新元素时,它会将其放置在索引 [size] 处,这与向量的 push_back 非常相似。它增加大小并返回新添加元素的索引。

  • 如果元素已经存在,则返回它出现的索引。

Set 似乎是理想的数据结构,但我没有看到任何类似 get 的东西 查找操作的索引。在集合上查找返回元素的迭代器。

在这种情况下,用 set.begin() 取不同是正确的做法吗?

【问题讨论】:

  • 如果从中间移除一个元素会怎样。它背后的元素的索引应该改变还是不应该改变?
  • 我认为@sbi 的目标是:如果索引在删除后发生变化,std::map 将完美运行。
  • @Thomas:我也这么认为,直到我意识到他想检查值是否已经存在。这是 std::map 中的 O(n)。
  • @sbi:有一段时间你几乎说服了我。但我们会将值映射到索引:std::map<ElementType, int>
  • @Thomas:但是按索引查找将是 O(n)。他要找的可能是一个多索引容器。

标签: c++ set containers indexed


【解决方案1】:

在 STL 中没有立即适用的数据结构,但实现这一点的一种直接且相当有效的方法是使用映射和指针向量。 map 将对象映射到它们在数组中的索引(以便检查对象是否存在是有效的,如果对象确实存在,则索引立即可用),vector 将索引映射到对象在地图中(以便通过索引检索对象是有效的)。

std::map<T,size_t> objects;
std::vector<const T *> indexed;

添加元素:

size_t add_element(const T &v) {
    std::map<T,size_t>::iterator it=objects.find(v);
    if(it==objects.end()) {
        it=objects.insert(std::map<T,size_t>::value_type(v,objects.size())).first;
        indexed.push_back(&it->first);
    }
    return it->second;
}

(根据个人风格明显的改动可能是存储一个map迭代器的向量,每次使用map::insert并检查结果的bool部分看indexed是否需要更新等)

并获取一个元素:

const T &get_element(size_t index) {
    return *indexed[index];
}

就是这样。一个问题当然是一旦对象在集合中,就不能修改它。这是一种从这里实现方式的泄漏,因为 map 键是 const 的,原因很明显——但事实上,不管实现如何,我认为无论如何它都是想要的。如果你坚持没有重复,那么一旦一个对象在列表中,它就不能被修改,以防任何修改会使它成为另一个对象的副本。

(另请注意,我在这里使用size_t 有点作弊——我想std::vector&lt;const T *&gt;::size_type 可能更准确——这主要是为了简洁!)

【讨论】: