C++技术点积累(8)——STL之算法汇总

C++技术点积累(8)——STL之算法汇总:

一、算法概述

1)算法部分主要由头文件<algorithm>,<numeric>和<functional>组成。

       <algorithm>是所有STL头文件中最大的一个,其中常用到的功能范围涉及到比较、交换、查找、遍历操作、复制、修改、反转、排序、合并等等。
       <numeric>体积很小,只包括几个在序列上面进行简单数学运算的模板函数,包括加法和乘法在序列上的一些操作。
       <functional>中则定义了一些模板类,用以声明函数对象(仿函数)。
       STL提供了大量实现算法的模版函数,只要我们熟悉了STL之后,许多代码可以被大大的化简,只需要通过调用一两个算法模板,就可以完成所需要的功能,从而大大地提升效率。
       #include <algorithm>,#include <numeric>,#include <functional>
2)STL中算法分类
      操作对象 
         直接改变容器的内容
         将原容器的内容复制一份,修改其副本,然后传回该副本
      功能: 
         a.非可变序列算法 指不直接修改其所操作的容器内容的算法
             计数算法 count、count_if
             搜索算法 search、find、find_if、find_first_of、…
             比较算法 equal、mismatch、lexicographical_compare
         b.可变序列算法 指可以修改它们所操作的容器内容的算法
             删除算法 remove、remove_if、remove_copy、…
             修改算法 for_each、transform
             排序算法 sort、stable_sort、partial_sort、
         c.排序算法 包括对序列进行排序和合并的算法、搜索算法以及有序序列上的集合操作

         d.数值算法 对容器内容进行数值计算

二、函数对象、谓词、函数适配器

       从概念上说,函数对象是用作函数的对象;但从实现上说,函数对象是实现了operator()的类的对象。虽然函数和函数指针也可以归为函数对象,但实现了operator()的类的对象才能保存状态(即类的成员属性的值)(见下面main02()函数),才能用于STL算法。

      接受一个参数的函数,叫做一元函数,如果一元函数返回布尔值,则该函数成为一元谓词;接受两个参数的函数为二元函数,如果返回一个布尔值,则该函数称为二元谓词。


      A.函数对象: 
            重载函数调用操作符的类,其对象常称为函数对象(function object),即它们是行为类似函数的对象。一个类对象,表现出一个函数的特征,就是通过“对象名+(参数列表)”的方式使用一个类对象,如果没有上下文,完全可以把它看作一个函数对待。
            这是通过重载类的operator()来实现的。

            “在标准库中,函数对象被广泛地使用以获得弹性”,标准库中的很多算法都可以使用函数对象或者函数来作为自定的回调行为;


      B.谓词:
            一元函数对象:函数参数1个;
            二元函数对象:函数参数2个;
            一元谓词 函数参数1个,函数返回值是bool类型,可以作为一个判断式
谓词可以是一个仿函数,也可以是一个回调函数。
            二元谓词 函数参数2个,函数返回值是bool类型

#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)预定义函数对象基本概念:标准模板库STL提前定义了很多预定义函数对象,#include <functional> 必须包含。
        2)算术函数对象 
             预定义的函数对象支持加、减、乘、除、求余和取反。调用的操作符是与type相关联的实例
                   加法:plus<Types>
                             plus<string> stringAdd;
                             sres = stringAdd(sva1,sva2);
                  减法:minus<Types>
                  乘法:multiplies<Types>
                  除法:divides<Tpye>
                  求余:modulus<Tpye>
                  取反:negate<Type>
                            negate<int> intNegate;
                            ires = intNegate(ires);
                            Ires= UnaryFunc(negate<int>(),Ival1);
        3)关系函数对象 
                  等于:equal_to<Tpye>
                            equal_to<string> stringEqual;
                            sres = stringEqual(sval1,sval2);
              不等于:not_equal_to<Type>
                 大于:greater<Type>
          大于等于:greater_equal<Type>
                 小于:less<Type>
          小于等于:less_equal<Type>
        4)逻辑函数对象 
             逻辑与:logical_and<Type>
                           logical_and<int> indAnd;
                           ires = intAnd(ival1,ival2);
                           dres=BinaryFunc( logical_and<double>(),dval1,dval2);
             逻辑或:logical_or<Type>
             逻辑非:logical_not<Type>
                           logical_not<int> IntNot;
                           Ires = IntNot(ival1);

                           Dres=UnaryFunc( logical_not<double>,dval1);


        D.函数适配器

           常用函数函数适配器:
           标准库提供一组函数适配器,用来特殊化或者扩展一元和二元函数对象。常用适配器是:
           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;
}

三、常用的遍历算法

1)for_each() :用指定函数依次对指定范围内所有元素进行迭代访问,该函数不得修改序列中的元素。函数定义:For_each(begin, end, func);第三个参数 函数对象做函数参数,函数对象做返回值。——也就是说可以用另外一个函数对象去接for_each的返回值,从而记录函数对象参数的状态。

     CshowV1 cv = for_each(v1.begin(), v1.end(), CshowV1());    //在CshowV1 这个类中可以定义一些状态
2)transform()的作用 :可以一个容器的元素,通过op,变换到另一个容器中(同一个容器中) ,也可以把两个容器的元素,通过op,变换到另一个容器中
        注意: 1.如果目标与源相同,transform()就和for_each()一样。     2.如果想以某值替换符合规则的元素,应使用replace()算法。

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

 
 结论:           
           for_each()——速度快 不灵活   ;  transform()——速度慢非常灵活
           一般情况下:for_each: 所使用的函数对象,参数是引用,没有返回值 

                                transform:所使用的函数对象,参数一般不使用引用,而是还有返回值


四、常用的查找算法

adjacent_find()

在iterator对标识元素范围内,查找一对相邻重复元素,找到则返回指向这对元素的第一个元素的迭代器。否则返回past-the-end。

vector<int>::iterator it = adjacent_find(vecInt.begin(), vecInt.end());

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 的值的个数(自定义)

五、常用的排序算法 

merge() 

merge:    合并两个有序序列,存放到另一个序列。

merge(vecIntA.begin(),vecIntA.end(),vecIntB.begin(),vecIntB.end(),vecIntC.begin());//生成的C,仍旧是有序的


sort() 
sort:  以默认升序的方式重新排列指定范围内的元素。若要改排序规则,可以输入比较函数。
sort(vecStu.begin(),vecStu.end(),Compare);  //Compare自定义谓词,还可以是自定义函数对象


void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )

int compare (const void *elem1, const void *elem2 ) );

qsort(quicksort)主要根据你给的比较条件给一个快速排序,主要是通过指针移动实现排序功能。排序之后的结果仍然放在原来数组中。

参数意义如下:

base:需要排序的目标数组开始地址

num:目标数组元素个数

width:目标数组中每一个元素长度

compare:函数指针,指向比较函数

 

一、对int类型数组排序

int num[100];

int cmp ( const void *a , const void *b )

{

return *(int *)a – *(int *)b;

可见:参数列表是两个空指针,现在他要去指向你的数组元素。所以转型为你当前的类型,然后取值。升序排列。

因为是按照acs码的值进行的,所以在前面的字符的值一定小于后面的字符。

那么,对于a b,如果a的值>b的值,就说明在字母表中a在b的后面,返回值为1表示ture,执行交换。

}

qsort(num,100,sizeof(num[0]),cmp);

 

二、对char类型数组排序(同int类型)

char word[100];

int cmp( const void *a , const void *b )

{

return *(char *)a – *(int *)b;

}

qsort(word,100,sizeof(word[0]),cmp);

 

三、对double类型数组排序(特别要注意)

double in[100];

int cmp( const void *a , const void *b )

{

return *(double *)a > *(double *)b ? 1 : -1;

返回值的问题,显然cmp返回的是一个整型,所以避免double返回小数而被丢失。 

来一个判断。 

}

qsort(in,100,sizeof(in[0]),cmp);


random_shuffle()——随机排序
random_shuffle(v1.begin(), v1.end());//基础数据类型的vector
string str = “abcdefg”;
random_shuffle(str.begin(), str.end());


reverse()——倒序排列

六、常用的拷贝和替换算法

copy() 

copy(vecIntA.begin(), vecIntA.end(), vecIntB.begin());  //把A拷贝到B


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);  //交换

七、常用的算术和生成算法 

accumulate() ——#include<numeric>

accumulate:  对指定范围内的元素求和,然后结果再加上一个由val指定的初始值。

int iSum = accumulate(vecIntA.begin(), vecIntA.end(), 100);


fill() 
fill:   将输入值赋给标志范围内的所有元素。
fill(vecIntA.begin(), vecIntA.end(), 8);

八、常用的集合算法

set_union:  构造一个有序序列,包含两个有序序列的并集

     set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), v3.begin());


set_intersection:  构造一个有序序列,包含两个有序序列的交集

set_difference:  构造一个有序序列,该序列保留第一个有序序列中存在而第二个有序序列中不存在的元素(差集)。

九、STL算法综合演练

需求:
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);
}

十、常用算法一览表

1)查找算法:

《C++技术点积累(8)——STL之算法汇总》

《C++技术点积累(8)——STL之算法汇总》

《C++技术点积累(8)——STL之算法汇总》

2)堆算法:

《C++技术点积累(8)——STL之算法汇总》

3)关系算法

《C++技术点积累(8)——STL之算法汇总》
《C++技术点积累(8)——STL之算法汇总》

《C++技术点积累(8)——STL之算法汇总》

4)集合算法

《C++技术点积累(8)——STL之算法汇总》

《C++技术点积累(8)——STL之算法汇总》

5)列组合算法

《C++技术点积累(8)——STL之算法汇总》

6)排序和通用算法

《C++技术点积累(8)——STL之算法汇总》

《C++技术点积累(8)——STL之算法汇总》

《C++技术点积累(8)——STL之算法汇总》
《C++技术点积累(8)——STL之算法汇总》

《C++技术点积累(8)——STL之算法汇总》

7)删除和替换算法

《C++技术点积累(8)——STL之算法汇总》

《C++技术点积累(8)——STL之算法汇总》
《C++技术点积累(8)——STL之算法汇总》

8)生成和遍历算法

《C++技术点积累(8)——STL之算法汇总》

9)算数算法

《C++技术点积累(8)——STL之算法汇总》

点赞