删除向量的元素

Posted

技术标签:

【中文标题】删除向量的元素【英文标题】:deleting elements of a vector 【发布时间】:2012-03-23 07:56:56 【问题描述】:

我有一个向量用于存储类的所有对象(由new 创建)的指针。 对于每个对象,我都有一个 int 成员变量(称为 id),它存储了存储该对象地址的向量元素的索引。 这个方法可以帮助我在 id 的帮助下调用任何对象。 id 是在静态变量的帮助下在构造函数中分配的,该变量在每次创建对象时递增。 我的问题是,当我删除一个对象时,我无法删除向量元素(释放该元素占用的内存,因为我的向量是动态创建的)(因为如果我删除它,那么所有下一个元素的索引将减少1)。 所以我想知道一种方法,通过它我可以从向量中释放内存,但不会导致其他元素的索引发生变化。 请告诉我一个不消耗大量时间的方法。 (诸如重新排列元素然后更改每个对象的 id 之类的方法会消耗大量时间!) 我可能有大约 1000 个这样的对象,如果我删除第一个元素并如上所述进行重新排列,这可能会消耗大量时间。 谢谢你

【问题讨论】:

1) 使用段落,2) 发布一些代码。 重新索引一千个对象根本不会花费很长时间(!)。重新索引 1000,000 可能是值得优化的事情。无论如何,您可以保留两个向量,一个包含已删除的索引和原始向量。每次删除项目时,将其内容设置为 null 并将索引添加到已删除索引集中。每当您添加新项目时,您首先检查已删除索引列表中是否有空闲索引。如果有,则重用该索引,否则将一个添加到向量中。 是的。一)使用段落。 II)发布一些代码。 康拉德的想法要好得多。 【参考方案1】:

只需要重新排列一个元素:swapvector的最后一个元素到要删除的元素的位置,然后删除最后一个元素。

int to_delete = …;
swap(my_vec[to_delete], my_vec[my_vec.end() - 1]);
my_vec[to_delete].index = to_delete;
delete my_vec.back();

【讨论】:

好主意。不是我的第一个想法。 @Konrad 但是如果我将最后一个元素的 id 存储在某处可能会在交换后流入流中怎么办。我猜这会导致错误,因为那个 id 将不存在或可能引用其他元素 +1 如果您需要确保对象的 id 永远不会改变,请查看我的答案。 @CAD_coding 啊,你没提到这个。好吧,很明显,如果你不能找到所有出现的索引,那么你就不能重新排列这些项目。 为什么你会存储这些索引呢?为什么不维护指向对象的指针呢? @CAD_coding:但无论如何您都必须传递 ID!传递ID或地址类似。【参考方案2】:

你的设计很不寻常。如果您希望对象的位置保持不变,则使用矢量不是您的最佳选择。您可以将对象存储在列表、集合或地图中,当一个对象被删除时,它们都会保留对象的位置。这样你就可以通过指针而不是 id 来访问对象(尽管使用 map 你可以同时使用两者)

如果您出于某种原因必须使用矢量,您可以使用“交换和弹出”技巧来删除对象。当一个对象被删除时,将它与向量中的最后一个元素交换,然后调用 pop_back() 删除最后一个元素。然后,您需要使用新 ID 更新刚刚移动的元素。但是操作是在恒定时间内运行的。

【讨论】:

【参考方案3】:

您可能需要考虑使用map<int,your_object_type*> 而不是vector<your_object_type*>。然后,您无需在删除项目时“重新索引”或(更重要的是,我认为)重新分配 ID。

【讨论】:

因为我需要经常迭代并且向量最擅长,我决定使用vectots @CAD_coding 地图在迭代方面也不错。我不认为它比向量慢得多。它确实给出了不好的局部性,但由于你的向量只包含指针,而不是对象本身,所以无论如何你已经得到了。主要区别在于查找:从向量中的键查找对象几乎是瞬时的,O(1) 具有非常非常小的常数;在地图中查找对象是 O(lg n),具有更大的常数因子。 @JamesKanze 所以假设我在地图中有 1000 个元素,那么使用 operator[int] 搜索元素需要多少时间....int 是关键 访问map 是 O(ln n);根据我的经验,对于只有 1000 个元素的数据集,这通常不是问题。然而,常数因子很大。如果您可以控制整数值,您可能最好使用vector,并找到一些重用已删除元素索引的方法。【参考方案4】:

有第二个可重复使用的插槽/ID 列表,每次新建元素时首先检查它们。删除元素时,会将元素的 id 添加到可重用 id 列表中:

//Somewhere in the code
vector<Object*> s_objects;
vector<int> s_freeIds;

//On new:
if (s_freeIds.empty())

   s_objects.push_back(new Object());
   s_objects.back().id = s_objects.size() - 1;

else

    int id = s_freeIds.back();
    s_freeIds.pop_back();
    s_objects[id] = new Object();
    s_objects[id].id = id;


//On delete
s_freeIds.push_back(this->id);

【讨论】:

【参考方案5】:

最简单的解决方案可能是不从向量中删除元素 完全没有,但只需用空指针替换它。这确实意味着 遍历完整向量时,必须检查 null 指针,但这通常不是一个大问题。并且为了防止 向量从无限增长,你必须能够重用插槽; 要么你在向量上扫描空指针 (std::find) 插入,或者您维护某种空闲插槽列表。

请注意,如果您的 id 被用作向量的索引,您 不想独立生成它。如果您插入使用 push_back(因为向量中没有空指针),id 是v.size() - 1push_back 之后,或者只是v.size() 之前;如果 您使用返回的迭代器在特定位置插入 std::find,那么标识符就是那个迭代器 - v.begin()。如果 您只是在使用线性搜索(并且向量小至 1000 元素,这可能就足够了),然后类似于以下内容 应该工作:

//      returns index of inserted element
int
insertIntoVector( std::vector<MyType*>& index, MyType* newObject )

    std::vector<MyType*>::iterator position
            = std::find( index.begin(), index.end(), NULL );
    int results = position - index.begin();
    if ( position == index.end() ) 
        index.push_back( newObject );
     else 
        *position = newObject;
    
    return results;

如果您要缓存空闲插槽,请将 std::find 替换为 对应的代码找到空闲槽。

【讨论】:

【参考方案6】:

真正的问题:你为什么使用vector

首先,关于生成 ID:

typedef size_t ID;

static ID& accessID()  static ID id = 0; return id; 

ID currentID()  return accessID(); 
ID newID()  return ++accessID(); 

其次,这是典型的工厂情况,所以让我们提供一个Factory 实现。

class ObjectFactory 
  typedef std::unordered_map<ID, Object> Register;

public:
  Object& create() 
    ID const id = newID();
    return register.insert(std::make_pair(id, Object(id))).first->second;
  

  Object* access(ID id) 
    Register::iterator it = register.find(id);
    return it == register.end() ? nullptr : &it->second;
  

  Object const* get(ID id) const 
    Register::const_iterator it = register.find(id);
    return it == register.end() ? nullptr : &it->second;
  

  bool remove(ID id) 
    return register.erase(id);
  

private:
  Register register;
;

您可以选择将当前 ID 设为工厂的成员,或者将其分开,以防您想要不同的工厂并且害怕混淆 ID。

【讨论】:

考虑到 michael 的回答,我现在决定使用 map。无论如何谢谢你

以上是关于删除向量的元素的主要内容,如果未能解决你的问题,请参考以下文章

从向量中删除元素时如何减小向量的大小?

分段错误:从二维向量中随机删除元素

使用迭代器 C++ 删除对象类型的向量元素

从向量结构中删除元素[关闭]

R:删除向量的最后一个元素

如何从向量中随机删除一个元素?