算法
介绍
1.STL将算法库分成4组:
- 非修改式序列操作。(对区间中的每个元素进行操作,但不修改容器的内容。例如:find(),for_each())
- 修改式序列操作。(对区间中的每个元素进行操作,可以修改内容、值、值的排列顺序。例如:transform()、randm_shuffle()、copy())
- 排序和相关操作。(包括多个排序函数,例如:sort(),其他各种函数,包括集合操作)
- 通用数字运算。(将区间的内容积累、计算两个容器的内部乘积、计算小计、计算相连对象差的函数。都是数组的操作特性,vector可能使用这些操作)。
算法主要是由头文件 <algorithm> <functional> <numeric>
组成。
- 是所有STL头文件中最大的一个,范围涉及到比较、 交换、查找、遍历操作、复制、修改等等
- 体积很小,只包括几个在序列上面进行简单数学运算的模板函数
- 定义了一些模板类,用以声明函数对象。
常用遍历算法
1.for_each:遍历容器元素
#include <algorithm>
#include<iostream>
#include <vector>
using namespace std;
//普通函数
void print01(int val)
{
cout << val << " ";
}
//函数对象
class print02
{
public:
void operator()(int val)
{
cout<< val << " ";
}
};
//for_each算法基本用法
void test01()
{
vector<int> v;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
//遍历算法
for_each(v.begin(), v.end(), print01);
cout << endl;
for_each(v.begin(),v.end(),print02());
cout<<endl;
}
int main()
{
test01();
return 0;
}
2.transform算法 将指定容器区间元素搬运到另一容器中
#include <algorithm>
#include<iostream>
#include <vector>
using namespace std;
int myTransInt1(int val)
{
return val;
}
class myTransInt2
{
public:
int operator ()(int val)
{
return val;
}
};
int main()
{
vector<int> v1;
v1.push_back(10);
v1.push_back(20);
v1.push_back(30);
v1.push_back(40);
//将v1容器的元素 把运到v2中
vector<int> v2;
//预先:设置v2的大小(重要)
v2.resize(v1.size());
//transform(v1.begin(),v1.end(),v2.begin(), 搬运方式);
//transform(v1.begin(),v1.end(),v2.begin(),myTransInt);
transform(v1.begin(),v1.end(),v2.begin(),myTransInt2());
for_each(v2.begin(),v2.end(),[](int val){cout<<val<<" ";});
cout<<endl;
}
常用查找算法
1.find算法查找元素
#include <algorithm>
#include<iostream>
#include <vector>
#include<string>
using namespace std;
class Person
{
public:
string name;
int age;
public:
Person(string name, int age)
{
this-> name = name;
this-> age = age;
}
bool operator==(const Person & ob)
{
if (this-> name == ob.name && this->age == ob.age)
return true;
return false;
}
};
int main()
{
vector<int> v1;
v1.push_back(10);
v1.push_back(20);
v1.push_back(30);
v1.push_back(40);
vector<int>::iterator ret;
ret = find(v1.begin(), v1.end(), 20);
if (ret != v1.end())
{
cout << "找到的数据为:" << *ret << endl;
}
vector<Person> v2;
v2.push_back(Person("小华", 18));
v2.push_back(Person("小法", 19));
v2.push_back(Person("Tom", 20));
v2.push_back(Person("小明", 21));
Person tmp("Tom", 20);
vector<Person>::iterator ret2; //定义了一个名为ret2的变量,它的数据类型是由vector<int>定义的iterator类型。
//对于find寻找自定义数据 需要重载==
ret2 = find(v2.begin(),v2.end(),tmp);
if(ret2 != v2.end())
{
cout<<"找到的数据name="<<(*ret2).name<<",age="<<(*ret2).age<<endl;
}
}
总结: 利用find可以在容器中找指定的元素,返回值是迭代器
对于find寻找自定义数据 需要重载==
find_if条件查找
#include <algorithm>
#include<iostream>
#include <vector>
#include<string>
using namespace std;
bool myGreaterThan20(int val)
{
return val>20;
}
class MyGreaterThan20
{
public:
bool operator ()(int val)
{
return val>20;
}
};
int main()
{
vector<int> v1;
v1.push_back(10);
v1.push_back(20);
v1.push_back(30);
v1.push_back(40);
//寻找第一个大于20的数
vector<int>::iterator ret;//定义了一个名为ret的变量,它的数据类型是由vector<int>定义的iterator类型。
//ret=find_if(v1.begin(),v1.end(),myGreaterThan20);
ret=find_if(v1.begin(),v1.end(),MyGreaterThan20());
if(ret !=v1.end())
{
cout<<"寻找到数据为:"<<*ret<<endl;
}
}
总结:find_if按条件查找使查找更加灵活,提供的仿函数可以改变不同的策略
adjacent_find算法 查找相邻重复元素
#include<algorithm>
#include<iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(5);
v.push_back(2);
v.push_back(4);
v.push_back(4);
v.push_back(3);
//查找相邻重复元素
vector<int>::iterator it = adjacent_find(v.begin(), v.end());
if (it == v.end()) {
cout << "找不到!" << endl;
}else
{
cout << "找到相邻重复元素为:" << *it << endl;
}
}
总结:面试题中如果出现查找相邻重复元素,记得用STL中的adjacent_find算法
binary_search算法 二分查找法(容器必须有序)
#include<algorithm>
#include<iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);
v.push_back(50);
bool ret=binary_search(v.begin(),v.end(),30);
if(ret==true)
{
cout<<"找到"<<endl;
}else
{
cout<<"没找到"<<endl;
}
}
总结:二分查找法查找效率很高,值得注意的是查找的容器中元素必须的有序序列
count算法统计元素出现次数
#include<algorithm>
#include<iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v;
v.push_back(10);
v.push_back(20);
v.push_back(10);
v.push_back(40);
v.push_back(50);
cout<<count(v.begin(),v.end(),10)<<endl;
}
统计自定义数据类型时候,需要配合重载operator==
#include<algorithm>
#include<iostream>
#include <vector>
#include<string>
using namespace std;
class Person
{
public:
string name;
int age;
public:
Person(string name, int age)
{
this->name = name;
this->age = age;
}
bool operator ==(const Person& p)
{
if (this->age == p.age)
{
return true;
}
else
{
return false;
}
}
};
int main()
{
vector<Person> v;
v.push_back(Person("Tom", 35));
v.push_back(Person("小关", 35));
v.push_back(Person("小张", 35));
v.push_back(Person("Lucy", 30));
v.push_back(Person("小明", 25));
Person tmp("小华",20);
int num = count(v.begin(), v.end(), tmp);
cout << "num = " << num << endl;
}
count_if算法 统计元素出现次数
#include<algorithm>
#include<iostream>
#include <vector>
#include<string>
using namespace std;
bool myGreaterThan10(int val)
{
return val>10;
}
class MyGreaterThan10
{
public:
bool operator ()(int val)
{
return val>10;
}
};
int main()
{
vector<int> v1;
v1.push_back(10);
v1.push_back(20);
v1.push_back(10);
v1.push_back(40);
v1.push_back(10);
//普通函数
cout<<count_if(v1.begin(),v1.end(),myGreaterThan10)<<endl;
//函数对象
cout<<count_if(v1.begin(),v1.end(),MyGreaterThan10())<<endl;
//内建函数对象
cout<<count_if(v1.begin(),v1.end(),bind2nd(greater<int>(),10))<<endl;
}
常用排序算法
1.merge算法 容器元素合并,并存储到另一容器中(每个容器必须有 序)
#include<algorithm>
#include<iostream>
#include <vector>
using namespace std;
class myPrint
{
public:
void operator ()(int val)
{
cout<<val<<" ";
}
};
int main()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(3);
v1.push_back(5);
v1.push_back(7);
vector<int> v2;
v2.push_back(2);
v2.push_back(4);
v2.push_back(6);
v2.push_back(8);
//预先:设置v3的大小
vector<int> v3;
v3.resize(v1.size()+v2.size());
//合并 需要两个有序序列
merge(v1.begin(),v1.end(),v2.begin(),v2.end(),v3.begin());
for_each(v3.begin(),v3.end(),myPrint());
cout<<endl;
}
总结:merge合并的两个容器必须的有序序列。
sort算法 容器元素排序
#include<algorithm>
#include<iostream>
#include <vector>
using namespace std;
void myPrint(int val)
{ cout << val << " ";
}
void test01()
{
vector<int> v;
v.push_back(10);
v.push_back(30);
v.push_back(50);
v.push_back(20);
v.push_back(40);
//sort默认从小到大排序
sort(v.begin(), v.end());
for_each(v.begin(), v.end(), myPrint);
cout << endl;
//从大到小排序
sort(v.begin(), v.end(), greater<int>());
for_each(v.begin(), v.end(), myPrint);
cout << endl;
}
int main()
{
test01();
return 0;
}
random_shuffle算法 对指定范围内的元素随机调整次序
#include<algorithm>
#include<iostream>
#include <vector>
#include<time.h>
using namespace std;
int main()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(3);
v1.push_back(5);
v1.push_back(7);
//srand设置随机种子
srand(time(NULL));
for_each(v1.begin(),v1.end(),[](int val){cout<<val<<" ";});
cout<<endl;
//打乱顺序
random_shuffle(v1.begin(),v1.end());
for_each(v1.begin(),v1.end(),[](int val){cout<<val<<" ";});
cout<<endl;
}
总结:random_shuffle洗牌算法比较实用,使用时记得加随机数种子
reverse算法 反转指定范围的元素
include<algorithm>
#include<iostream>
#include <vector>
using namespace std;
class myPrint
{
public:
void operator()(int val)
{
cout << val << " ";
}
};
void test01()
{
vector<int> v;
v.push_back(10);
v.push_back(30);
v.push_back(50);
v.push_back(20);
v.push_back(40);
cout << "反转前: " << endl;
for_each(v.begin(), v.end(), myPrint());
cout << endl; cout << "反转后: " << endl;
reverse(v.begin(), v.end());
for_each(v.begin(), v.end(), myPrint());
cout << endl;
}
int main()
{
test01();
return 0;
}
总结:reverse反转区间内元素,面试题可能涉及到
常用拷贝和替换算法
1、copy算法 将容器内指定范围的元素拷贝到另一容器中
#include<algorithm>
#include<iostream>
#include <vector>
#include<iterator>
using namespace std;
int main()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(3);
v1.push_back(5);
v1.push_back(7);
vector<int> v2;
//预先:设置大小
v2.resize(v1.size());
copy(v1.begin(),v1.end(),v2.begin());
for_each(v2.begin(),v2.end(),[](int val){cout<<val<<" ";});
cout<<endl;//1 3 5 7
//:用copy输出(了解)
copy(v2.begin(),v2.end(), ostream_iterator<int>(cout," ") );
}
2.replace算法 将容器内指定范围的旧元素修改为新元素
#include<algorithm>
#include<iostream>
#include <vector>
using namespace std;
class myPrint
{
public:
void operator()(int val)
{ cout << val << " ";
}
};
int main()
{
vector<int> v;
v.push_back(20);
v.push_back(30);
v.push_back(20);
v.push_back(40);
v.push_back(50);
v.push_back(10);
v.push_back(20);
cout << "替换前:" << endl;
for_each(v.begin(), v.end(), myPrint());
cout << endl;
//将容器中的20 替换成 2000
cout << "替换后:" << endl;
replace(v.begin(), v.end(), 20,2000);
for_each(v.begin(), v.end(), myPrint());
cout << endl;
}
3.replace_if算法 将容器内指定范围满足条件的元素替换为新元素
include<algorithm>
#include<iostream>
#include <vector>
using namespace std;
class myPrint
{
public:
void operator()(int val)
{
cout << val << " ";
}
};
class ReplaceGreater30
{
public:
bool operator()(int val)
{
return val >= 30;
}
};
int main()
{
vector<int> v;
v.push_back(20);
v.push_back(30);
v.push_back(20);
v.push_back(40);
v.push_back(50);
v.push_back(10);
v.push_back(20);
cout << "替换前:" << endl;
for_each(v.begin(), v.end(), myPrint());
cout << endl; //将容器中大于等于的30 替换成 3000
cout << "替换后:" << endl;
replace_if(v.begin(), v.end(), ReplaceGreater30(), 3000);
for_each(v.begin(), v.end(), myPrint());
cout << endl;
}
总结:replace_if按条件查找,可以利用仿函数灵活筛选满足的条件。
4.swap算法 互换两个容器的元素
#include<algorithm>
#include<iostream>
#include <vector>
using namespace std;
class myPrint
{
public:
void operator()(int val)
{
cout << val << " ";
}
};
int main()
{
vector<int> v1;
vector<int> v2;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
v2.push_back(i+100);
}
cout << "交换前: " << endl;
for_each(v1.begin(), v1.end(), myPrint());
cout << endl;
for_each(v2.begin(), v2.end(), myPrint());
cout << endl; cout << "交换后: " << endl;
swap(v1, v2);
for_each(v1.begin(), v1.end(), myPrint());
cout << endl;
for_each(v2.begin(), v2.end(), myPrint());
cout << endl;
}
五级标题
算术生成算法
1、accumulate算法 计算容器元素累计总和
#include<algorithm>
#include<iostream>
#include <vector>
#include<numeric>
using namespace std;
int main()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(3);
v1.push_back(5);
v1.push_back(7);
int sum=accumulate(v1.begin(),v1.end(),100);
cout<<"sum="<<sum<<endl;
}
总结:accumulate使用时头文件注意是 numeric,这个算法很实用。
2、fill算法 向容器中添加元素
#include<algorithm>
#include<iostream>
#include <vector>
#include<iterator>
using namespace std;
int main()
{
vector<int> v;
v.resize(5);
fill(v.begin(),v.end(),100);
copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));
cout<<endl;
}
总结:利用fill可以将容器区间内元素填充为 指定的值。
常用集合算法
1、setintersection算法 求两个set集合的交集
2、set_union算法 求两个set集合的并集
3、set_difference算法 求两个set集合的差集
例:
#include<algorithm>
#include<iostream>
#include <vector>
#include<iterator>
using namespace std;
int main()
{
vector<int> A;
A.push_back(1);
A.push_back(3);
A.push_back(5);
A.push_back(7);
A.push_back(9);
vector<int> B;
B.push_back(7);
B.push_back(9);
B.push_back(2);
B.push_back(4);
B.push_back(6);
//求交集
vector<int> v1;
//存放交集
v1.resize(min(A.size(),B.size()));
vector<int>::iterator myEnd= set_intersection(A.begin(),A.end(),B.begin(),B.end(), v1.begin());
copy(v1.begin(), myEnd, ostream_iterator<int>(cout," ") );
cout<<endl;
//求并集
vector<int> v2;//存放并集
v2.resize(A.size()+B.size() );
myEnd = set_union(A.begin(),A.end(),B.begin(),B.end(), v2.begin());
copy(v2.begin(), myEnd, ostream_iterator<int>(cout," ") );
cout<<endl;
//求差集 A 差 B
vector<int> v3;//存放并集
v3.resize( A.size() );
myEnd = set_difference(A.begin(),A.end(),B.begin(),B.end(), v3.begin());
copy(v3.begin(), myEnd, ostream_iterator<int>(cout," ") );
cout<<endl;
}
STL比赛练习
比赛规则: 某市举行一场演讲比赛( speech_contest ),共有24个人参加。比赛共三 轮,前两轮为淘汰赛,第三轮为决赛。
比赛方式:分组比赛,每组6个人;选手每次要随机分组,进行比赛;
第一轮分为4个小组,每组6个人。比如编号为: 100-123. 整体进行抽签 (draw)后顺序演讲。当小组演讲完后,淘汰组内排名最后的三个选手,然后 继续下一个小组的比赛。
第二轮分为2个小组,每组6人。比赛完毕,淘汰组内排名最后的三个选手,然 后继续下一个小组的比赛。
第三轮只剩下1组6个人,本轮为决赛,选出前三名。 比赛评分:10个评委打 分,去除最低、最高分,求平均分每个选手演讲完由10个评委分别打分。该选 手的最终得分是去掉一个最高分和一个最低分,求得剩下的8个成绩的平均分。 选手的名次按得分降序排列。 用STL编程,求解这个问题 1) 请打印出所有选手的名字与参赛号,并以参赛号 的升序排列。 2) 打印每一轮比赛后,小组比赛成绩和小组晋级名单 需求分析: 1) 产生选手 ( ABCDEFGHIJKLMNOPQRSTUVWX ) 姓名、得 分;
选手编号
第1轮 选手抽签 选手比赛 查看比赛结果
第2轮 选手抽签 选手比赛 查看比赛结果
第3轮 选手抽签 选手比赛 查看比赛结果
实现思路:
需要把选手信息、选手得分信息、选手比赛抽签信息、选手的晋级 信息保存在容器中,需要涉及到各个容器的选型。 选手可以设计一个类 Speaker(姓名和得分) 所有选手的编号可以单独放在一个vector容器中,做 抽签用 所有选手编号和选手信息,可以放在容器内
#include <iostream>
#include<string.h>
#include<iterator>
#include<vector>//编号容器
#include<map>//编号,选手
#include<algorithm>
#include<time.h>
#include<deque>
using namespace std;
class Speaker
{
public:
string name;
int score[3];
public:
Speaker()
{
}
Speaker(string name)
{
this->name = name;
memset(score, 0, sizeof(score));//string.h
}
};
void createSpeaker(vector<int>& v, map<int, Speaker>& m)
{
string tmp = "ABCDEFGHIJKLMNOPQRSTUVWX";
for (int i = 0; i < 24; i++)
{
//存放选手编号
v.push_back(100 + i);
//存放《编号,选手》
string name = "选手";
name += tmp[i];
m.insert(make_pair(i + 100, Speaker(name)));
}
}
void printSpeechResault(int index, vector<int>& v, map<int, Speaker>& m, vector<int>& v1)
{
cout << "第" << index << "轮比赛成绩如下" << endl;
int count = 0;
int n = 0;
vector<int>::iterator mit = v1.begin();
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
if (count % 6 == 0)
{
n = count / 6 + 1;
cout << "第" << n << "组的成绩如下:" << endl;
}
count++;
cout << "姓名:" << m[*it].name << ", 得分:" << m[*it].score[index-1] << endl;
//每个组的成绩打印完 立马打印晋级名单
if (count % 6 == 0)
{
cout << "第" << n << "组的晋级名单如下:" << endl;
for (int i = 0; i < 3; i++, mit++)
{
cout << "姓名:" << m[*mit].name << ", 得分:" << m[*mit].score[index-1] << endl;
}
}
}
}
void speech_contest(int index, vector<int>& v, map<int, Speaker>& m, vector<int>& v1)
{
int count = 0;
//设计一个<分数,编号>的map容器 存放每一组的《分数,编号》
multimap<int, int, greater<int>> m2;
//选手逐一登台比赛
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
count++;
//*it 代表一名选手编号
//10个评委打分
deque<int> d;
for (int i = 0; i < 10; i++)
{
int score = rand() % 41 + 60;//60~100
d.push_back(score);
}
//对d容器排序
sort(d.begin(), d.end());
//去掉最高分 以及最低分
d.pop_back();
d.pop_front();
//求取总分数
int sum = accumulate(d.begin(), d.end(), 0);
//求平均分 并赋值给选手的score[index‐1]
//map<int,Speaker>
int avg = sum / d.size();
(m[*it]).score[index-1] = avg;
m2.insert(make_pair(avg, *it));
if (count % 6 == 0)//刚好已经有6人 把上面的6人的成绩取前3
{
//90 80 70 60 50 40
//遍历m2容器 取出前3名的 编号
int i = 0;
for (multimap<int, int, greater<int>>::iterator mit = m2.begin(); \
mit != m2.end() && i < 3; mit++, i++)
{
//将晋级的编号 放入晋级的容器v1中
v1.push_back((*mit).second);
}
//将上一组的m2清空
m2.clear();//清空
}
}
}
int main(int argc, char* argv[])
{
//创建24名选手、将选手<编号,选手>放入map容器中 选手编号放vector<>容器中
vector<int> v;//选手编号
map<int, Speaker> m;//<编号,选手>
//创建选手24名
createSpeaker(v, m);
//设置种子
srand(time(NULL));
//第一轮:参加的选手抽签
random_shuffle(v.begin(), v.end());
//进行第一轮比赛
//1表示当前轮数 v选手编号 m选手信息 v1晋级容器
vector<int> v1;
speech_contest(1, v, m, v1);
//打印第一轮比赛结果:所有参与比赛的成绩 晋级的名单
printSpeechResault(1, v, m, v1);
//第二轮比赛:参加的选手抽签
random_shuffle(v1.begin(), v1.end());
//进行第二轮比赛
//1表示当前轮数 v选手编号 m选手信息 v1晋级容器
vector<int> v2;
speech_contest(2, v1, m, v2);
//打印第一轮比赛结果:所有参与比赛的成绩 晋级的名单
printSpeechResault(2, v1, m, v2);
//第二轮比赛:参加的选手抽签
random_shuffle(v2.begin(), v2.end());
//进行第二轮比赛
//1表示当前轮数 v选手编号 m选手信息 v1晋级容器
vector<int> v3;
speech_contest(3, v2, m, v3);
//打印第一轮比赛结果:所有参与比赛的成绩 晋级的名单
printSpeechResault(3, v2, m, v3);
return 0;
}