删除向量的元素
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】:只需要重新排列一个元素:swap
vector的最后一个元素到要删除的元素的位置,然后删除最后一个元素。
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() - 1
在push_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。无论如何谢谢你以上是关于删除向量的元素的主要内容,如果未能解决你的问题,请参考以下文章