我正在查看 stl 矢量的 API 文档,并注意到矢量类上没有允许删除具有特定值的元素的方法。这似乎是一种常见的操作,而且没有内置的方法来做到这一点似乎很奇怪。
11 回答
std::remove
实际上并没有从容器中删除元素,但它确实返回了新的结束迭代器,可以传递给它container_type::erase
来真正删除现在位于容器末尾的额外元素:
std::vector<int> vec;
// .. put in some values ..
int int_to_remove = n;
vec.erase(std::remove(vec.begin(), vec.end(), int_to_remove), vec.end());
如果你想删除一个项目,下面会更有效率。
std::vector<int> v;
auto it = std::find(v.begin(), v.end(), 5);
if(it != v.end())
v.erase(it);
或者,如果订单对您无关紧要,您可以避免移动物品的开销:
std::vector<int> v;
auto it = std::find(v.begin(), v.end(), 5);
if (it != v.end()) {
using std::swap;
// swap the one to be removed with the last element
// and remove the item at the end of the container
// to prevent moving all items after '5' by one
swap(*it, v.back());
v.pop_back();
}
使用带有 begin 和 end 迭代器的全局方法 std::remove,然后使用 std::vector.erase 实际删除元素。
文档链接
std::remove http://www.cppreference.com/cppalgorithm/remove.html
std::vector.erase http://www.cppreference.com/cppvector/erase.html
std::vector<int> v;
v.push_back(1);
v.push_back(2);
//Vector should contain the elements 1, 2
//Find new end iterator
std::vector<int>::iterator newEnd = std::remove(v.begin(), v.end(), 1);
//Erase the "removed" elements.
v.erase(newEnd, v.end());
//Vector should now only contain 2
感谢 Jim Buck 指出我的错误。
从c++20开始:
引入了一个非成员函数std::erase
,它将要删除的向量和值作为输入。
前任:
std::vector<int> v = {90,80,70,60,50};
std::erase(v,50);
如果你有一个未排序的向量,那么你可以简单地与最后一个向量元素交换 then resize()
。
对于有序容器,最好使用 <code>std::vector::erase()。请注意,在 中有一个std::remove()
定义<algorithm>
,但实际上并没有进行擦除。(仔细阅读文档)。
其他答案涵盖了如何做好这件事,但我想我还想指出,这不在向量 API 中并不奇怪:它是低效的,通过向量线性搜索值,然后是一堆复制以将其删除。
如果您正在密集地执行此操作,则可能值得考虑 std::set 出于这个原因。
另请参阅std::remove_if以能够使用谓词...
这是上面链接中的示例:
vector<int> V;
V.push_back(1);
V.push_back(4);
V.push_back(2);
V.push_back(8);
V.push_back(5);
V.push_back(7);
copy(V.begin(), V.end(), ostream_iterator<int>(cout, " "));
// The output is "1 4 2 8 5 7"
vector<int>::iterator new_end =
remove_if(V.begin(), V.end(),
compose1(bind2nd(equal_to<int>(), 0),
bind2nd(modulus<int>(), 2)));
V.erase(new_end, V.end()); [1]
copy(V.begin(), V.end(), ostream_iterator<int>(cout, " "));
// The output is "1 5 7".
一个更短的解决方案(不会强迫您重复向量名称 4 次)是使用 Boost:
#include <boost/range/algorithm_ext/erase.hpp>
// ...
boost::remove_erase(vec, int_to_remove);
*
C++ 社区已经听到了您的请求 :)
*
C++ 20现在提供了一种简单的方法。它变得如此简单:
#include <vector>
...
vector<int> cnt{5, 0, 2, 8, 0, 7};
std::erase(cnt, 0);
您应该检查std::erase和std::erase_if。
它不仅会删除值的所有元素(此处为“0”),而且会以O(n)的时间复杂度进行。这是你能得到的最好的。
如果你的编译器不支持 C++ 20,你应该使用erase-remove idiom:
#include <algorithm>
...
vec.erase(std::remove(vec.begin(), vec.end(), 0), vec.end());
如果您想在没有任何额外内容的情况下执行此操作,则包括:
vector<IComponent*> myComponents; //assume it has items in it already.
void RemoveComponent(IComponent* componentToRemove)
{
IComponent* juggler;
if (componentToRemove != NULL)
{
for (int currComponentIndex = 0; currComponentIndex < myComponents.size(); currComponentIndex++)
{
if (componentToRemove == myComponents[currComponentIndex])
{
//Since we don't care about order, swap with the last element, then delete it.
juggler = myComponents[currComponentIndex];
myComponents[currComponentIndex] = myComponents[myComponents.size() - 1];
myComponents[myComponents.size() - 1] = juggler;
//Remove it from memory and let the vector know too.
myComponents.pop_back();
delete juggler;
}
}
}
}
有两种方法可以用来特别擦除一个项目。让我们拿一个向量
std :: vector < int > v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);
v.push_back(40);
v.push_back(50);
1)非有效方式:虽然它看起来很有效,但这并不是因为擦除函数删除元素并将所有元素向左移动1。 所以它的复杂度将是O(n ^ 2)
std :: vector < int > :: iterator itr = v.begin();
int value = 40;
while ( itr != v.end() )
{
if(*itr == value)
{
v.erase(itr);
}
else
++itr;
}
2)高效方式(推荐):也称为ERASE-REMOVE成语。
- std::remove 将给定范围转换为一个范围,其中所有比较不等于给定元素的元素都移动到容器的开头。
- 所以,实际上不要删除匹配的元素。它只是将不匹配的内容转移到开始并将迭代器提供给新的有效结束。它只需要 O(n) 复杂度。
删除算法的输出是:
10 20 30 50 40 50
因为 remove 的返回类型是指向该范围新端的迭代器。
template <class ForwardIterator, class T>
ForwardIterator remove (ForwardIterator first, ForwardIterator last, const T& val);
现在使用向量的擦除功能从向量的新端到旧端删除元素。它需要 O(1) 时间。
v.erase ( std :: remove (v.begin() , v.end() , element ) , v.end () );
所以这个方法在 O(n) 中工作