【问题标题】:How do I remove an item from a stl vector with a certain value?如何从具有特定值的 stl 向量中删除项目?
【发布时间】:2010-09-07 13:59:19
【问题描述】:

我正在查看 stl vector 的 API 文档,并注意到 vector 类上没有允许删除具有特定值的元素的方法。这似乎是一种常见的操作,而且没有内置的方法来执行此操作似乎很奇怪。

【问题讨论】:

标签: c++ stl


【解决方案1】:

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());

【讨论】:

  • 这种多合一语句的形式是否取决于编译器评估参数的顺序,或者vec.end() 保证在调用std::remove 的任一侧都相同?在我看来,像这样阅读网络的其他部分是安全的,但应该清楚地说明。
  • 没关系。 vec.end() 的结果不需要相同;它只需要正确(它是正确的)。
  • vec.end() 确实需要相同,但这没关系,因为std::remove 不会改变它。如果它确实改变了它(并使旧值无效),那么就会出现问题:参数的评估顺序未指定,因此您不知道第二个 vec.end() 在使用时是否仍然有效。相同的原因很简单,std::remove 不会改变容器的大小,它只是移动内容。
  • 我发现这个问题很重要,因为我也有同样的问题。但是,我的视觉工作室只接受一个参数std::remove;那是const char *_Filename。我需要调用什么方法?
  • 即删除文件的remove 版本。您需要包含&lt;algorithm&gt; 才能访问处理容器的remove 版本。
【解决方案2】:

如果你想删除一个项,下面会更高效一点。

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();
}

【讨论】:

  • 请注意,如果重复项存在,则不会删除它们,而 std::remove_if 方法可以。
【解决方案3】:

将全局方法 std::remove 与 begin 和 end 迭代器一起使用,然后使用 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 指出我的错误。

【讨论】:

  • 这可以防止其他元素在删除向量中间的元素时移动,因此速度更快。它首先将它们移动到向量的末尾,然后您可以从向量的末尾丢弃。
【解决方案4】:

来自c++20

引入了一个非成员函数std::erase,它将要移除的向量和值作为输入。

例如:

std::vector<int> v = {90,80,70,60,50};
std::erase(v,50);

【讨论】:

  • 不仅如此,它还为每个特定的容器超载!
  • ... 并利用容器特定的属性,例如 map::erase!
【解决方案5】:

其他答案涵盖了如何做好这一点,但我想我还想指出,这不在向量 API 中并不奇怪:它效率低下,通过向量线性搜索值,然后通过一堆复制来删除它。

如果您正在密集地执行此操作,出于这个原因,可能值得考虑使用 std::set。

【讨论】:

    【解决方案6】:

    如果你有一个未排序的向量,那么你可以简单地与最后一个向量元素交换然后resize()

    使用订购的容器,最好使用 ‍std::vector::erase()。请注意,&lt;algorithm&gt; 中定义了一个std::remove(),但这实际上并没有进行擦除。 (仔细阅读文档)。

    【讨论】:

      【解决方案7】:

      更短的解决方案(不会强迫您重复向量名称 4 次)是使用 Boost:

      #include <boost/range/algorithm_ext/erase.hpp>
      
      // ...
      
      boost::remove_erase(vec, int_to_remove);
      

      http://www.boost.org/doc/libs/1_64_0/libs/range/doc/html/range/reference/algorithms/new/remove_erase.html

      【讨论】:

        【解决方案8】:

        *

        C++ 社区已经听到了您的请求 :)

        *

        C++ 20 现在提供了一种简单的方法。 它变得如此简单:

        #include <vector>
        ...
        vector<int> cnt{5, 0, 2, 8, 0, 7};
        std::erase(cnt, 0);
        

        您应该查看std::erasestd::erase_if

        它不仅会删除该值的所有元素(此处为“0”),而且会在 O(n) 时间复杂度内完成。这是你能得到的最好的。

        如果你的编译器不支持 C++ 20,你应该使用erase-remove idiom:

        #include <algorithm>
        ...
        vec.erase(std::remove(vec.begin(), vec.end(), 0), vec.end());
        

        【讨论】:

          【解决方案9】:

          另请参阅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".
          

          【讨论】:

          • 请在这篇文章中添加更多细节。就目前而言,它的大部分内容都来自一个链接,如果链接断开,就会丢失。
          • bind2nd 是什么事实 -(在 C++11 中已弃用)(在 C++17 中已删除)
          【解决方案10】:

          如果你想在没有任何额外内容的情况下这样做:

          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;
                      }
                  }
              }
          }
          

          【讨论】:

            【解决方案11】:

            有两种方法可以用来特别擦除一个项目。 让我们取一个向量

            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 idioms

            • 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) 中工作

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多