C++技术点积累(8)——STL之算法汇总:
#include<iostream> #include<string> #include<vector> #include<list> #include<set> #include<functional> #include<algorithm> using namespace std; //1 一元函数对象 template <typename T> class ShowElemt { public: ShowElemt() { n = 0; } void operator()(T& t) { n++; print(); cout << "成员函数()" << t << endl; } void print() { cout << n <<endl; } private: int n; }; //2 函数模板 ====函数 template <class T> void FuncShowElemt(T &t) { cout << "函数模板<>:" << t << endl; } //3 普通函数 void FuncShowElemt2(int &t) { cout << "普通函数:" << t << endl; } //4 直接调用 函数对象 void main01() { int a = 10; ShowElemt<int> show1;//函数对象 show1(a); //show1.operator()(a); FuncShowElemt<int>(a); FuncShowElemt2(a); } //5 函数对象 做 函数参数,以及for_each()函数 void main02() { vector<int> v1; v1.push_back(1); v1.push_back(2); v1.push_back(3); for_each(v1.begin(), v1.end(), ShowElemt<int>());//匿名函数对象,匿名仿函数 cout << endl; for_each(v1.begin(), v1.end(), FuncShowElemt2); cout << endl; //函数对象 做 函数参数 ShowElemt<int> show1; /*调试跟踪 调出 for_each函数原型: template<class _InIt,class _Fn1> inline _Fn1 for_each(_InIt _First, _InIt _Last, _Fn1 _Func)//_Fn1 _Func可以看出这是个元素,而不是一个引用 { // perform function for each element _DEBUG_RANGE(_First, _Last); _DEBUG_POINTER(_Func); return (_For_each(_Unchecked(_First), _Unchecked(_Last), _Func)); } */ //结论:for_each算法的 函数对象的传递 是元素值传递 ,不是引用传递 for_each(v1.begin(), v1.end(), show1);//使用函数对象 //从for_each函数原型中的_Fn1 _Func可以看出这是个元素,而不是一个引用 //所以在for_each中对show1的操作 show1.print(); //打印的n仍为0,没有把上面的n信息变化的状态记录下来 //还要注意一下函数返回值 cout << endl; cout << "通过for_each算法的返回值看调用的次数:" << endl; show1 = for_each(v1.begin(), v1.end(), show1); show1.print(); //打印3 //要点: 分清楚 stl算法返回的值是迭代器 还是 谓词(函数对象) 是stl算法入门的重要点 } //6 一元谓词,以及find_if()函数 template <typename T> class isDiv { public: isDiv(const T &divisor) { this->divisor = divisor; } bool operator()(T &t) { return (t % divisor == 0); } private: T divisor; }; void main03() { vector<int> v2; for (int i = 10; i < 33; i++) { v2.push_back(i); } int a = 4; isDiv<int> myDiv(a); vector<int>::iterator it; /* template<class _InIt,class _Pr> inline _InIt find_if(_InIt _First, _InIt _Last, _Pr _Pred) { // find first satisfying _Pred _DEBUG_RANGE(_First, _Last); _DEBUG_POINTER(_Pred); return (_Rechecked(_First,_Find_if(_Unchecked(_First), _Unchecked(_Last), _Pred))); } //find_if返回值是一个迭代器 //要点: 分清楚 stl算法返回的值是迭代器 还是 谓词(函数对象) 是stl算法入门的重要点 */ it = find_if(v2.begin(), v2.end(), isDiv<int>(a)); if (it == v2.end()) { cout << "容器中没有被 4 整除的元素" << endl; } else cout << "容器中第一个被 4 整除的的元素的是:" << *it << endl; } //7 二元函数对象,以及transform()函数 template <class T> class Add { public: T operator()(T t1, T t2) { return t1 + t2; } }; void main04() { vector<int> v1, v2; vector<int> v3; v1.push_back(1); v1.push_back(3); v1.push_back(4); v2.push_back(2); v2.push_back(4); v2.push_back(6); v3.resize(10); //重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。 /* template<class _InIt1,class _InIt2,class _OutIt,class _Fn2> inline _OutIt transform(_InIt1 _First1, _InIt1 _Last1,_InIt2 _First2, _OutIt _Dest, _Fn2 _Func) { // transform [_First1, _Last1) and [_First2, ...) with _Func _DEBUG_RANGE(_First1, _Last1); _DEBUG_POINTER(_Dest); _DEBUG_POINTER(_Func); if (_First1 != _Last1) return (_Transform2(_Unchecked(_First1), _Unchecked(_Last1),_First2, _Dest, _Func,_Is_checked(_Dest))); return (_Dest); } //transform 把运算结果的迭代器的开始位置 返出来 */ //v1+v2,存到v3,因为下面的transform接收了Add transform(v1.begin(), v1.end(), v2.begin(), v3.begin(), Add<int>()); cout << "v3: "; for (vector<int>::iterator it = v3.begin(); it != v3.end(); it++) { cout << *it << " "; } cout << endl; } //8 二元谓词 bool MyCompare(const int &a, const int &b) { return a < b;//从小到大 } void main05() { vector<int> v1(10); for (int i = 0; i < v1.size(); i++) { int tmp = rand() % 100; v1[i] = tmp; } cout << "rand随机生成序列:"; for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++) { cout << *it << " "; } cout << endl << "sort排序生成序列:"; sort(v1.begin(), v1.end(), MyCompare); for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++) { cout << *it << " "; } cout << endl; } //9 二元谓词在set集合中的应用 struct CompareNoCase { bool operator()(const string &str1, const string &str2) { //不区分大小写,只需要把传过来的东西进行统一 string str1Tmp; str1Tmp.resize(str1.size()); transform(str1.begin(), str1.end(), str1Tmp.begin(), tolower); //STL预定义函数对象tolower string str2Tmp; str2Tmp.resize(str2.size()); transform(str2.begin(), str2.end(), str2Tmp.begin(), tolower); //STL预定义函数对象tolower return (str1Tmp < str2Tmp); // 从小到大进行排序 } }; void main06() { set<string> set1; set1.insert("bbb"); set1.insert("ccc"); set1.insert("aaa"); set<string>::iterator it = set1.find("aaa"); if (it == set1.end()) { cout << "集合中不存在“aaa”!" << endl; } else cout << "集合中存在“aaa”!" << endl; set<string, CompareNoCase> set2; set2.insert("bbb"); set2.insert("ccc"); set2.insert("aaa"); set<string, CompareNoCase>::iterator it2 = set2.find("aAA"); if (it2 == set2.end()) { cout << "不区分大小写,集合中不存在“aaa”!" << endl; } else cout << "不区分大小写,查找到“aAA”!" << endl; } void main() { //main01(); //函数对象基本概念 //main02(); //函数对象的好处 函数对象做函数参数 函数对象做返回值 //main03(); //一元谓词 //main04(); //二元函数对象 //main05(); //二元谓词 main06(); //二元谓词在set集合中的应用 }C.预定义函数对象
常用函数函数适配器:
标准库提供一组函数适配器,用来特殊化或者扩展一元和二元函数对象。常用适配器是:
1绑定器(binder): binder通过把二元函数对象的一个实参绑定到一个特殊的值上,将其转换成一元函数对象。C++标准库提供两种预定义的binder适配器:bind1st和bind2nd,前者把值绑定到二元函数对象的第一个实参上,后者绑定在第二个实参上。
2取反器(negator) : negator是一个将函数对象的值翻转的函数适配器。标准库提供两个预定义的ngeator适配器:not1翻转一元预定义函数对象的真值,而not2翻转二元谓词函数的真值。
常用函数适配器列表如下:
bind1st(op, value)
bind2nd(op, value)
not1(op)
not2(op)
mem_fun_ref(op)
mem_fun(op)
ptr_fun(op)
函数适配器引入:
void main11() { vector<string> v1; v1.push_back("aaa");v1.push_back("ccc");v1.push_back("ddd");v1.push_back("bbb");v1.push_back("ccc"); //equal_to<string>()有两个参数: left参数来自前面的容器v1,right参数来自后面的sc //bind2nd函数适配器:把预定义函数对象 和 第二个参数进行绑定 string strC = "ccc"; int num = count_if(v1.begin(), v1.end(), bind2nd( equal_to<string>(),strC)); cout << "num:" << num << endl; }函数适配器综合案例:
//通过谓词 求大于2的个数 class isGreater { public: isGreater(int i) { m_num = i; } bool operator()(int &num) { return num > m_num ? true : false; } private: int m_num; }; void main22() { vector<int> v1; for (int i = 0; i < 10; i++) { v1.push_back(i + 1); } for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++) { cout << *it << " "; } cout << endl; int num = count(v1.begin(), v1.end(), 3); cout << "num3:" << num << endl; //通过 谓词 求大于2的个数 int num2 = count_if(v1.begin(), v1.end(), isGreater(2)); cout << "num>2:" << num2 << endl; //通过 预定义的函数对象 求大于2的个数 //greater<int>() 有两个参数 左参数来自容器的元素 ,右参数固定成2 (通过bind2nd做的) int num3 = count_if(v1.begin(), v1.end(), bind2nd(greater<int>(), 2)); cout << "num>2:" << num3 << endl; //求 奇数的个数 int num4 = count_if(v1.begin(), v1.end(), bind2nd(modulus<int>(), 2)); cout << "奇数的个数:" << num4 << endl; //求 偶数的个数——使用取反器(negator) int num5 = count_if(v1.begin(), v1.end(), not1(bind2nd(modulus<int>(), 2))); cout << "偶数的个数:" << num5 << endl; }
transform 对 函数对象的要求:——要有返回值!而for_each的函数对象不需要!
参见对正在编译的函数 模板 实例化 “_OutIt std::_Transform1<int*,_OutIt, void(__cdecl *)(int &)> (_InIt,_InIt,_OutIt,_Fn1, std::tr1::true_type)”的引用 1> with 1> [ 1> _OutIt=std::_Vector_iterator<std::_Vector_val<int,std::allocator<int>>>, 1> _InIt=int *, 1> _Fn1=void (__cdecl *)(int &) 1> ]
上述_Transform1会调用下面的_Transform:——跟踪调试可知道! template<class _InIt, class _OutIt, class _Fn1> inline _OutIt _Transform(_InIt _First, _InIt _Last, _OutIt _Dest, _Fn1 _Func) {// transform [_First, _Last) with _Func for (; _First != _Last; ++_First, ++_Dest) *_Dest = _Func(*_First); //解释了 为什么 函数对象要有返回值——返回一个void话,*_Dest=void自然会报错 return (_Dest); }结论:
binary_search——二分法查找
在有序序列中查找value,找到则返回true。注意:在无序序列中,不可使用。
bool bFind = binary_search(setInt.begin(),setInt.end(),5);
count()
利用等于操作符,把标志范围内的元素与输入值比较,返回相等的个数。
int iCount = count(vecInt.begin(),vecInt.end(),2);
count_if()
int iCount = count_if(vecIntA.begin(), vecIntA.end(), GreaterThree);//大于3 的值的个数(自定义)
find()
find: 利用底层元素的等于操作符,对指定范围内的元素与输入值进行比较。当匹配时,结束搜索,返回该元素的迭代器。
equal_range: 返回一对iterator,第一个表示lower_bound,第二个表示upper_bound。
vector<int>::iterator it = find(vecInt.begin(), vecInt.end(), 5);
find_if()
find_if: 使用输入的函数代替等于操作符执行find。返回被找到的元素的迭代器。
vector<int>::iterator it = find_if(vecInt.begin(),vecInt.end(),GreaterThree);//大于3 的值的个数(自定义)
sort()
sort: 以默认升序的方式重新排列指定范围内的元素。若要改排序规则,可以输入比较函数。
sort(vecStu.begin(),vecStu.end(),Compare); //Compare自定义谓词,还可以是自定义函数对象
random_shuffle()——随机排序
random_shuffle(v1.begin(), v1.end());//基础数据类型的vector
string str = "abcdefg";
random_shuffle(str.begin(), str.end());
reverse()——倒序排列
replace()
replace(beg,end,oldValue,newValue): 将指定范围内的所有等于oldValue的元素替换成newValue。
replace_if()
replace_if : 将指定范围内所有操作结果为true的元素用新值替换。
replace_if(vecIntA.begin(),vecIntA.end(),GreaterThree,newVal); //GreaterThree 函数的原型是 bool GreaterThree(int iNum)
replace(v1.begin(), v1.end(), 3, 8);
// >=5
replace_if(v1.begin(), v1.end(), great_equal_5, 1);
swap()
swap: 交换两个容器的元素
swap(vecIntA, vecIntB); //交换
fill()
fill: 将输入值赋给标志范围内的所有元素。
fill(vecIntA.begin(), vecIntA.end(), 8);
需求:
1)某市举行一场演讲比赛( speech_contest ),共有24个人参加。比赛共三轮,前两轮为淘汰赛,第三轮为决赛。
2)比赛方式:分组比赛,每组6个人;选手每次要随机分组,进行比赛;
第一轮分为4个小组,每组6个人。比如100-105为一组,106-111为第二组,依次类推,
每人分别按照抽签(draw)顺序演讲。当小组演讲完后,淘汰组内排名最后的三个选手,然后继续下一个小组的比赛。
第二轮分为2个小组,每组6人。比赛完毕,淘汰组内排名最后的三个选手,然后继续下一个小组的比赛。
第三轮只剩下6个人,本轮为决赛,选出前三名。
3)比赛评分:10个评委打分,去除最低、最高分,求平均分
每个选手演讲完由10个评委分别打分。该选手的最终得分是去掉一个最高分和一个最低分,求得剩下的8个成绩的平均分。
选手的名次按得分降序排列,若得分一样,按参赛号升序排名。
实现思路:搭建框架——测试——完善业务函数——测试
需要把选手信息、选手得分信息、选手比赛抽签信息、选手的晋级信息保存在容器中,需要涉及到各个容器的选型。
选手可以设计一个类Speaker(姓名和得分)
所有选手编号和选手信息,可以放在容器内:map<int, Speaker>
所有选手的编号信息,可以放在容器:vecter<int> v1中
第1轮晋级名单,可以放在容器vecter<int> v2中
第2轮晋级名单,可以放在容器vecter<int> v3中
第3轮前三名名单,可以放在容器vecter<int> v4中
每个小组的比赛得分信息,按照从小到大的顺序放在 multimap<成绩, 编号, greater<int>> multmapGroup,也就是:multimap<int, int, greater<int> > multmapGroup;
每个选手的得分,放在容器deque<int> dscore; 方便去除最低最高分
实现:
#include <iostream> #include <vector> #include <string> #include <algorithm> #include <functional> #include <numeric> #include <map> #include <deque> #include "iterator" //输出流迭代器的头文件 using namespace std; class Speaker { public: string m_name; int m_score[3]; }; //产生选手 int GenSpeaker(map<int, Speaker> &mapSpeaker, vector<int> &v) { string str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; random_shuffle(str.begin(), str.end()); for (int i = 0; i<24; i++) { Speaker tmp; tmp.m_name = "选手"; tmp.m_name = tmp.m_name + str[i]; mapSpeaker.insert(pair<int, Speaker>(100 + i, tmp)); } for (int i = 0; i<24; i++) { v.push_back(100 + i); //参加比赛的人员 } return 0; } //选手抽签 int speech_contest_draw(vector<int> &v) { random_shuffle(v.begin(), v.end()); return 0; } //选手比赛 int speech_contest(int index, vector<int> &v1, map<int, Speaker> &mapSpeaker, vector<int> &v2) { //把 小组的比赛得分 记录下来;求出前三名 后3名 multimap<int, int, greater<int>> multmapGroup; //小组成绩 int tmpCount = 0; for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++) { tmpCount++; //打分 deque<int> dscore; for (int j = 0; j < 10; j++) //10个评委打分 { int score = 50 + rand() % 50; dscore.push_back(score); } sort(dscore.begin(), dscore.end()); dscore.pop_back(); dscore.pop_front(); //去除最低分 最高分 //求平均分 int scoresum = accumulate(dscore.begin(), dscore.end(), 0); //计算总分 int scoreavg = scoresum / dscore.size(); //平均分 mapSpeaker[*it].m_score[index] = scoreavg; //选手得分 存入选手信息容器中 multmapGroup.insert(pair<int, int>(scoreavg, *it)); //选手得分 存入小组信息容器中 //处理分组 if (tmpCount % 6 == 0) { cout << "小组的比赛成绩:" << endl; for (multimap<int, int, greater<int>>::iterator mit = multmapGroup.begin(); mit != multmapGroup.end(); mit++) { //编号 姓名 得分 cout << mit->second << "\t" << mapSpeaker[mit->second].m_name << "\t" << mit->first << endl; } //前三名晋级 while (multmapGroup.size() > 3) //multimap按greater<int>排列 { multimap<int, int, greater<int>>::iterator it1 = multmapGroup.begin(); v2.push_back(it1->second); //把前三名 放到v2 晋级名单 中 multmapGroup.erase(it1); //erase的同时会自动后移 } multmapGroup.clear(); //清空本小组 比赛成绩 } } return 0; }; //查看比赛结果 int speech_contest_print(int index, vector<int> &v, map<int, Speaker> &mapSpeaker) { printf("第%d轮 晋级名单:\n", index + 1); for (vector<int>::iterator it = v.begin(); it != v.end(); it++) { cout << "参赛编号: " << *it << "\t" << mapSpeaker[*it].m_name << "\t" << mapSpeaker[*it].m_score[index] << endl; } return 0; }; void main() { //容器的设计 map<int, Speaker> mapSpeaker; //参加比赛的选手 vector<int> v1; //第1轮 演讲比赛 名单 vector<int> v2; //第2轮 演讲比赛 名单 vector<int> v3; //第3轮 演讲比赛 名单 vector<int> v4; //最后前三名 演讲比赛 名单 //产生选手 得到第一轮选手的比赛名单 GenSpeaker(mapSpeaker, v1); //第1轮 选手抽签 选手比赛 打印比赛结果 cout << "任意键,开始第1轮比赛" << endl; cin.get(); speech_contest_draw(v1); speech_contest(0, v1, mapSpeaker, v2); speech_contest_print(0, v2, mapSpeaker); //第2轮 选手抽签 选手比赛 打印比赛结果 cout << "\n\n\n任意键,开始第2轮比赛" << endl; cin.get(); speech_contest_draw(v2); speech_contest(1, v2, mapSpeaker, v3); speech_contest_print(1, v3, mapSpeaker); //第3轮 选手抽签 选手比赛 打印比赛结果 cout << "\n\n\n任意键,开始第3轮比赛" << endl; cin.get(); speech_contest_draw(v3); speech_contest(2, v3, mapSpeaker, v4); speech_contest_print(2, v4, mapSpeaker); }