STL的remove算法详解

来源:互联网 发布:软件售前文档 编辑:程序博客网 时间:2024/06/05 10:52
一、remove的声明:
template<class ForwardIterator, class T>

ForwardIterator remove( ForwardIterator first,  ForwardIterator last,  const T& value)


remove结论:remove并不“真的”删除东西,因为它做不到。

注意:list::remove除外


二、remove详细过程

1、举例:

vector<int> v;                  
v.reserve(10);                  
for (int i = 1; i <= 10; ++i) {
        v.push_back(i);
}

// 打印10
cout << v.size(); 

// 设置3个元素为99                      
v[3] = v[5] = v[9] = 99;

// 删除所有等于99的元素                
remove(v.begin(), v.end(), 99); 

// 仍然是10!
cout << v.size();      


v在调用remove前看起来的样子:



如果我们把remove的返回值存放在一个叫做newEnd的新迭代器中:
vector<int>::iterator newEnd(remove(v.begin(), v.end(), 99));
这是调用后v看起来的样子:


这里我用问号来标明那些在概念上已经从v中被删除,但继续存在的元素的值。


调用完remove后,在我知道的所有实现中,v看起来像这样:


正如你所见,两个曾经存在于v的“99”不再在那儿了,而一个“99”仍然存在。一般来说,调用完remove后,从区间中删除的值可能是也可能不在区间中继续存在。大多数人觉得这很奇怪,但为什么?


2、remove行为

你可以想象remove完成了一种压缩,被删除的值表演了在压缩中被填充的洞的角色。对于我们的vector v,它按照下面的表演:
1).  remove检测v[0],发现它的值不是要被删除的,然后移动到v[1]。同样的情况发生在v[1]和v[2]。


2.  发现v[3]应该被删除,所以它记录下v[3]的值应该被覆盖,然后它移动到v[4]。这类似记录v[3]是一个条款32:如果你真的想删除东西的话就在类似remove的算法后接上erase需要填充的“洞”。


3.  发现v[4]的值应该被保持,所以它把v[4]赋给v[3],记录下v[4]应该被覆盖,然后移动到v[5]。继续类
似的压缩,它用v[4]“填充”v[3]而且记录v[4]现在是一个洞。


4.  发现v[5]应该被删除,所以忽略并它移动到v[6]。仍然记得v[4]是一个等待填充的洞。


5.  发现v[6]是一个应该保留的值,所以把v[6]赋给v[4]。记得v[5]现在是下一个要被填充的洞,然后移到v[7]。


6.  在某种意义上类似上面的,检查v[7]、v[8]和v[9]。把v[7]赋给v[5],v[8]赋给v[6],忽略v[9],因为v[9]的值是要被删除的。


7.  返回指定下一个要被覆盖的元素的迭代器,在这个例子中这个元素是v[7]。


你可以预想在v中值的移动情况像这样:

 


三、remove删除元素的正确用法  

// 正如从前

vector<int> v;    

// 真的删除所有等于99的元素                                     

v.erase(  remove(v.begin(), v.end(), 99), v.end()  );               
// 现在返回7                                                      
cout << v.size();    


把remove的返回值作为erase区间形式第一个实参传递很常见,这是个惯用法。事实上,remove和erase是亲密联盟    


四、list::remove  

// 建立一个list

ist<int> li;                   
// 放一些值进去

...

// 除去所有等于99的元素:真的删除元素,所以它的大小可能改变了
li.remove(99);                 

对于list,调用remove成员函数比应用erase-remove惯用法更高效


remove和remove_if之间的相似性很直截了当。所以我不会细讲,但unique行为也像remove。它用来从一个区间删除东西(邻近的重复值)而不用访问持有区间元素的容器。结果,如果你真的要从容器中删除元素,你也必须成对调用unique和erase,unique在list中也类似于remove。正像list::remove真的删除东西(而且比erase-
remove惯用法高效得多)。list::unique也真的删除邻近的重复值(也比erase-unique高效)。                                

0 0