前面一节我们学习了区间交换算法swap_ranges()函数,读者是否还记得它的功能是什么吗?没错,该函数能够高效地交换两个同类型且等长的区间。本节我们将学习一个新的算法——移除算法,顾名思义,移除算法指的是快速移动同目标元素相等的元素到非相等元素区间,注意“移除”并非删除,想要实现删除效果需要配合erase()函数才行。remove()函数就是一个重要的移除函数,”remove“的意思是”移除“,读者切勿认为它是”再一次移动“,从名称上就能看出这个函数的功能是把同目标元素相等的元素”挪“到后面非相等元素区间。

remove()函数的语法格式如下:

template< class ForwardIt, class T >
ForwardIt remove( ForwardIt first, ForwardIt last, const T& value );

remove(first,last,val)函数指的是对于区间[first,last),我们把同val值相等的元素移动到”后面“,有返回值,类型为迭代器,指向最后一个未被移除元素之后的位置。比如我们有{1,2,3,2,4,2,5,2},我们使用remove()函数移动”2“后序列就变为{1,3,4,5,2,2,2,2},返回的迭代器指向第一个”2“的位置。

下面我们通过代码来演示 remove(first,last,val)函数 的实际应用:

#include<iostream>
#include<vector>
#include<algorithm>
#include<iterator> //用于转换remove()函数返回值为下标 
#include<string>
using namespace std;
/*swap_ranges(first1, last1, first2) 函数*/
void test() {
     
    vector<string> v{"Dotcpp编程", "数据结构教程", "Dotcpp编程","C语言教程", "Dotcpp编程","算法竞赛教程"}; 
    
    /*移除前*/ 
    cout << "移除前:\n";
    cout << "v容器有字符串:";
    for_each(v.begin(), v.end(), [](const string& s){
        cout << s << " ";
    });
    cout << '\n';
    
    /*开始移除*/
    vector<string>::iterator pos =remove(v.begin(), v.end(), "Dotcpp编程");
    
    /*移除后*/ 
    cout << "移除之后:\n";
    cout << "v容器有字符串:";
    for_each(v.begin(), v.end(), [](const string& s){
        cout << s << " ";
    });
    cout << "\n\n";
    cout << "此时通过distance()迭代器函数我们得到第一个等于目标元素的下标为【"<< distance(v.begin(),pos)<< "】" ;
    
} 
int main(){
    system("title dotcpp.com");
    test();
    return 0;
}

编译结果如下:

remove()函数

可以看到,remove()函数把v容器“切割”成两个两个不同的区间,前一个区间不等于目标元素,后一个与目标元素完全相等,从数组的角度上看,它的下标正好是3,输出完全符合我们的预期。

当然,我们并非止步于学习一个移除算法,下面我们还会分别学到remove_copy()函数、remove_if ()函数和remove_copy_if()(函数。

先简要介绍一下这三个移除函数:

remove_copy() 函数可以将前两个正向迭代器参数指定的序列中的元素复制到第三个参数指定的目的序列中,并忽略和第 4 个参数相等的元素。它返回一个指向最后一个被复制到目的序列的元素的后一个位置的迭代器。序列不可重叠

remove_if()函数 可以从前两个正向迭代器指定的序列中移除能够使作为第三个参数的谓词返回 true 的元素。

remove_copy_if() 函数可以将前两个正向迭代器参数指定的序列中,能够使作为第 4 个参数的谓词返回 true 的元素,复制到第三个参数指定的目的序列中。它返回一个指向最后一个被复制到目的序列的元素的后一个位置的迭代器。序列不可重叠的。

1. 我们先看看remove_copy()函数,其语法格式如下:

template< class InputIt, class OutputIt, class T >
OutputIt remove_copy( InputIt first, InputIt last, OutputIt d_first, const T& value );

remove_copy(first1, last1 ,ret,val)函数的功能是把区间[first1,last1)内与val元素不相等的元素复制到以ret迭代器为起始迭代器的序列里,这里我们通过代码实操一下:

#include<iostream>
#include<vector>
#include<algorithm>
#include<iterator> 
#include<string>
using namespace std;
/*remove_copy(first1, last1 ,ret,val)函数*/
void test() {
     
    vector<string> v1{"Dotcpp编程", "数据结构教程", "Dotcpp编程","C语言教程", "Dotcpp编程","算法竞赛教程"}; //原序列
vector<string> v2;
/*remove_copy*/
remove_copy(v1.begin(),v1.end(),back_inserter(v2),"Dotcpp编程") ;
    
    cout << "v2容器有字符串:";
    for_each(v2.begin(), v2.end(), [](const string& s){
        cout << s << " ";
    });
    cout << '\n';
    
  
} 
int main(){
    system("title dotcpp.com");
    test();
    return 0;
}

编译结果如下:

remove_copy()函数

通过输出我们发现对于序列v1{"Dotcpp编程", "数据结构教程", "Dotcpp编程","C语言教程", "Dotcpp编程","算法竞赛教程"},我们使用remove_copy()函数将不等于"Dotcpp编程"的元素复制到v2,得到v2{ "数据结构教程", "C语言教程", "算法竞赛教程"},输出完全符合我们的预期。

2. 我们再看看remove_if()函数,其语法格式如下:

template< class ForwardIt, class UnaryPredicate >
ForwardIt remove_if( ForwardIt first, ForwardIt last, UnaryPredicate p );

remove_if(first, last, pred)函数的功能是移除区间[first1,last1)内将满足pred一元谓词的元素,这里我们通过代码实操一下:

#include<iostream>
#include<vector>
#include<algorithm>
#include<iterator> 
#include<string>
using namespace std;
/*remove_if(first, last, pred)函数*/
void test() {
     
    vector<string> v{"Dotcpp编程", "数据结构教程", "Dotcpp编程","C语言教程", "Dotcpp编程","算法竞赛教程"}; //原序列
/*移除长度>10的字符串*/ 
remove_if(v.begin(),v.end(),[](const string&s){
return s.size()>10;
}) ;
    cout << "v容器除长度>10的字符串后:";
    for_each(v.begin(), v.end(), [](const string& s){
        cout << s << " ";
    });
    cout << '\n';
    
  
} 
int main(){
    system("title dotcpp.com");
    test();
    return 0;
}

编译结果如下:

remove_if()函数

可以看到,由于Lambda表达式进行一元谓词判断字符串长度大于10,所以“数据结果教程”和“算法竞赛教程”被“挪”到后面了。

3. 最后,我们看看remove_copy_if()函数,其语法格式如下:

template< class InputIt, class OutputIt, class UnaryPredicate >
OutputIt remove_copy_if( InputIt first, InputIt last, OutputIt d_first, UnaryPredicate p );

在掌握remove_copy()函数和remove_if()函数的基础下,remove_copy_if(first1, last1, first2,pred)就很好理解了,函数通过first2起始迭代器复制区间[first1,last1)中所有不满足pred一元谓词的元素到目标容器,这里我们通过代码实操一下:

#include<iostream>
#include<vector>
#include<algorithm>
#include<iterator> 
#include<string>
using namespace std;
/*remove_copy_if(first1, last1, first2,pred)*/
void test() {
     
    vector<string> v1{"Dotcpp编程", "数据结构教程", "Dotcpp编程","C语言教程", "Dotcpp编程","算法竞赛教程"}; //原序列
    vector<string> v2; 
/*移除长度>10的字符串*/ 
remove_copy_if(v1.begin(),v1.end(),back_inserter(v2),[](const string&s){
return s.size()>10;
}) ;
    cout << "v2容器获得长度<=10的字符串后:";
    for_each(v2.begin(), v2.end(), [](const string& s){
        cout << s << " ";
    });
    cout << '\n';
    
  
} 
int main(){
    system("title dotcpp.com");
    test();
    return 0;
}

编译结果如下:

remove_copy_if()函数

通过输出可以看到,v2容器输出没有数据结果教程”和“算法竞赛教程”,因为他没一元谓词“过滤”了,输出完全符合预期。

总结:remove()函数能够移除与目标元素完全相等的元素,如果想要自定义比较规则可使用remove_if()函数,需要将与目标元素不等的元素复制到另外一个容器可以使用remove_copy(),如果既要定义比较规则又要复制不等元素那就使用remove_copy_if()函数。

点赞(0)

C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:

一点编程也不会写的:零基础C语言学练课程

解决困扰你多年的C语言疑难杂症特性的C语言进阶课程

从零到写出一个爬虫的Python编程课程

只会语法写不出代码?手把手带你写100个编程真题的编程百练课程

信息学奥赛或C++选手的 必学C++课程

蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程

手把手讲解近五年真题的蓝桥杯辅导课程

Dotcpp在线编译      (登录可减少运行等待时间)