上一节主要学习C++中的函数模版、数据结构以及排序查找操作:c++:-6,本节学习C++的范型程序设计和STL:
(1)用来界定具备一定功能的数据类型。例如:
(2)对于两个不同的概念A和B,如果概念A所需求的所有功能也是概念B所需求的功能,那么就说概念B是概念A的子概念。例如:
符合一个概念的数据类型称为该概念的模型,例如:
很多STL的实现代码就是使用概念来命名模板参数的。
为概念赋予一个名称,并使用该名称作为模板参数名。
例如:表示insertionSort这样一个函数模板的原型:
template
void insertionSort(Sortable a[], int n);
标准模板库(Standard Template Library,简称STL)提供了一些非常常用的数据结构和算法
更多参考:C++:STL
(1)* Iterators(迭代器)是算法和容器的桥梁。
(2)将函数对象作为算法的参数而不是将函数所执行的运算作为算法的一部分。
(3)使用STL中提供的或自定义的迭代器和函数对象,配合STL的算法,可以组合出各种各样的功能。
使用容器,需要包含对应的头文件
(1)STL为每个可逆容器都提供了逆向迭代器,逆向迭代器可以通过下面的成员函数得到:
rbegin() :指向容器尾的逆向迭代器
rend():指向容器首的逆向迭代器
(2)逆向迭代器的类型名的表示方式如下:
S::reverse_iterator:逆向迭代器类型
S::constreverseiterator:逆向常迭代器类型
随机访问容器支持对容器的元素进行随机访问
s[n]:获得容器s的第n个元素
STL包括70多个算法
例如:排序算法,消除算法,计数算法,比较算法,变换算法,置换算法和容器管理等
可以广泛用于不同的对象和内置的数据类型。
使用STL的算法,需要包含头文件
从标准输入读入几个整数,存入向量容器,输出它们的相反数
#include "iostream"
#include "vector"
#include "iterator"
#include "algorithm"
#include "functional"
using namespace std;
int main()
{
const int N=5;
vector<int> s(N);
for(int i=0;i<N;i++)
{
cin >> s[i];
}
transform(s.begin(),s.end(),ostream_iterator<int>(cout," "),negate<int>());
cout <<endl;
return 0;
}
transform算法的一种实现:
template <class InputIterator, class OutputIterator, class UnaryFunction>
OutputIterator transform(InputIterator first, InputIterator last, OutputIterator result, UnaryFunction op) {
for (;first != last; ++first, ++result)
*result = op(*first);
return result;
}
(1)迭代器是算法和容器的桥梁
(2)算法和容器独立
istream_iterator<T>
ostream_iterator<T>
二者都属于适配器
迭代器是泛化的指针,提供了类似指针的操作(诸如++、*、->运算符)
(1)输入迭代器
可以用来从序列中读取数据,如输入流迭代器
(2)输出迭代器
允许向序列中写入数据,如输出流迭代器
(3)前向迭代器
既是输入迭代器又是输出迭代器,并且可以对序列进行单向的遍历
(4)双向迭代器
与前向迭代器相似,但是在两个方向上都可以对数据遍历
(5)随机访问迭代器
也是双向迭代器,但能够在序列中的任意两个位置之间进行跳转,如指针、使用vector的begin()、end()函数得到的迭代
(1)两个迭代器表示一个区间:[p1, p2)
(2)STL算法常以迭代器的区间作为输入,传递输入数据
(3)合法的区间
p1经过n次(n > 0)自增(++)操作后满足p1 == p2
(4)区间包含p1,但不包含p2
(1)从标准输入读入几个实数,分别将它们的平方输出
#include <iterator>
#include <iostream>
#include <algorithm>
using namespace std;
//求平方的函数
double square(double x) {
return x * x;
}
int main() {
//从标准输入读入若干个实数,分别将它们的平方输出
transform(istream_iterator<double>(cin), istream_iterator<double>(),
ostream_iterator<double>(cout, "\t"), square);
cout << endl;
return 0;
}
输出:
1 2 3 4
1 4 9 16
(2) 综合运用几种迭代器的示例:程序涉及到输入迭代器、输出迭代器、随机访问迭代器这三个迭代器概念,并且以前两个概念为基础编写了一个通用算法。
#include <algorithm>
#include <iterator>
#include <vector>
#include <iostream>
using namespace std;
//模版迭代器
//将来自输入迭代器的n个T类型的数值排序,将结果通过输出迭代器result输出
template <class T, class InputIterator, class OutputIterator>
void mySort(InputIterator first, InputIterator last, OutputIterator result) {
//通过输入迭代器将输入数据存入向量容器s中
vector<T> s;
for (;first != last; ++first)
{
s.push_back(*first);//对s进行排序,sort函数的参数必须是随机访问迭代器
}
sort(s.begin(), s.end());
copy(s.begin(), s.end(), result); //将s序列通过输出迭代器输出
}
int main() {
//将s数组的内容排序后输出
double a[5] = { 1.2, 2.4, 0.8, 3.3, 3.2 };
mySort<double>(a, a + 5, ostream_iterator<double>(cout, " "));
cout << endl;
//从标准输入读入若干个整数,将排序后的结果输出
mySort<int>(istream_iterator<int>(cin), istream_iterator<int>(), ostream_iterator<int>(cout, " "));
cout << endl;
return 0;
}
上面代码有些问题。
包括:
以上四种在逻辑上可看作是一个长度可扩展的数组
(1)特点
(2)接口(不包含单向链表(forward_list)和数组(array))
(3)举例
#include <iostream>
#include <list>
#include <deque>
using namespace std;
//输出指定的顺序容器的元素
template <class T>
void printContainer(const char* msg, const T& s) {
cout << msg << ": ";
copy(s.begin(), s.end(), ostream_iterator<int>(cout, " "));//s.begin()指向s的第一个元素(头),s.end()指向s的最后一个元素(尾)的下一个
cout << endl;
}
int main() {
//从标准输入读入10个整数,将它们分别从s的头部加入(头)
deque<int> s;
for (int i = 0; i < 10; i++) {
int x;
cin >> x;
s.push_front(x);
}
printContainer("deque at first", s);
//用s容器的内容的逆序构造列表容器l
list<int> l(s.rbegin(), s.rend());//rbegin()指向最后一个元素(尾),rend()指向第一个元素(头)
printContainer("list at first", l);
//将列表容器l的每相邻两个元素顺序颠倒
/*
* 例如:1 2 3 4
* 先取v=1,然后删除1,即:2 3 4
* 然后将1插入到2后面,即:2 1 3 4
*/
list<int>::iterator iter = l.begin();//iter是双向链表
while (iter != l.end()) {
int v = *iter;
iter = l.erase(iter);//l.erase(iter)删除该位置,返回下一个位置
l.insert(++iter, v);//插入到最后
}
printContainer("list at last", l);
//用列表容器l的内容给s赋值,将s输出
s.assign(l.begin(), l.end());
printContainer("deque at last", s);
return 0;
}
输出:
1 2 3 4 5 6 7 8 9 10
deque at first: 10 9 8 7 6 5 4 3 2 1
list at first: 1 2 3 4 5 6 7 8 9 10
list at last: 2 1 4 3 6 5 8 7 10 9
deque at last: 2 1 4 3 6 5 8 7 10 9
队列:
列表:
顺序容器:向量、双端队列、列表、单向链表、数组
举例:先按照从大到小顺序输出奇数,再按照从小到大顺序输出偶数。
#include "iostream"
#include "vector"
#include "deque"
using namespace std;
int main() {
istream_iterator<int> i1(cin), i2; //建立一对输入流迭代器
vector<int> s1(i1, i2); //通过输入流迭代器从标准输入流中输入数据
sort(s1.begin(), s1.end()); //将输入的整数排序
deque<int> s2;
//以下循环遍历s1
for (vector<int>::iterator iter = s1.begin(); iter != s1.end(); ++iter)
{
if (*iter % 2 == 0) //偶数放到s2尾部
s2.push_back(*iter);
else //奇数放到s2首部
s2.push_front(*iter);
}
//将s2的结果输出
copy(s2.begin(), s2.end(), ostream_iterator<int>(cout, " "));
cout << endl;
return 0;
}
在任意位置插入和删除元素都很快
不支持随机访问
接合(splice)操作
举例:list结合
#include "iostream"
#include "string"
#include "list"
using namespace std;
int main() {
string names1[] = { "Alice", "Helen", "Lucy", "Susan" };
string names2[] = { "Bob", "David", "Levin", "Mike" };
//用names1数组的内容构造列表s1
list<string> s1(names1, names1 + 4);
//用names2数组的内容构造列表s2
list<string> s2(names2, names2 + 4);
//将s1的第一个元素放到s2的最后
s2.splice(s2.end(), s1, s1.begin());
list<string>::iterator iter1 = s1.begin(); //iter1指向s1首
advance(iter1, 2); //iter1前进2个元素,它将指向s1第3个元素
list<string>::iterator iter2 = s2.begin(); //iter2指向s2首
++iter2; //iter2前进1个元素,它将指向s2第2个元素
list<string>::iterator iter3 = iter2; //用iter2初始化iter3
advance(iter3, 2); //iter3前进2个元素,它将指向s2第4个元素
//将[iter2, iter3)范围内的结点接到s1中iter1指向的结点前
s1.splice(iter1, s2, iter2, iter3);
//分别将s1和s2输出
copy(s1.begin(), s1.end(), ostream_iterator<string>(cout, " "));
cout << endl;
copy(s2.begin(), s2.end(), ostream_iterator<string>(cout, " "));
cout << endl;
return 0;
}
输出:
Helen Lucy David Levin Susan
Bob Mike Alice
用于向容器头部、尾部或中间指定位置插入元素的迭代器
包括前插迭代器(frontinserter)、后插迭代器(backinsrter)和任意位置插入迭代器(inserter)
例如:
list
back_inserter iter(s);
*(iter++) = 5; //通过iter把5插入s末尾
以顺序容器为基础构建一些常用数据结构,是对顺序容器的封装
(1)栈模板
template <class T, class Sequence = deque<T> > class stack;
(2)队列模板
template <class T, class FrontInsertionSequence = deque<T> > class queue;
栈可以用任何一种顺序容器作为基础容器,而队列只允许用前插顺序容器(双端队列或列表)
(1)栈的操作
(2)队列操作
(3)举例:利用栈反向输出单词
#include "iostream"
#include "string"
#include "stack"
using namespace std;
int main() {
stack<char> s;
string str;
cin >> str; //从键盘输入一个字符串
//将字符串的每个元素顺序压入栈中
for (string::iterator iter = str.begin(); iter != str.end(); ++iter)
s.push(*iter);
//将栈中的元素顺序弹出并输出
while (!s.empty()) {
cout << s.top();//读头元素
s.pop();//取头元素
}
cout << endl;
return 0;
}
输出:
cmake-build-debug/untitled
deltitnu/gubed-dliub-ekamc
优先级队列也像栈和队列一样支持元素的压入和弹出,但元素弹出的顺序与元素的大小有关,每次弹出的总是容器中最“大”的一个元素。
template
优先级队列的基础容器必须是支持随机访问的顺序容器。
支持栈和队列的size、empty、push、pop几个成员函数,用法与栈和队列相同。
优先级队列并不支持比较操作。
与栈类似,优先级队列提供一个top函数,可以获得下一个即将被弹出元素(即最“大”的元素)的引用。
特点:
每个关联容器都有一个键(key)
可以根据键高效地查找元素
(1)单重关联容器(set和map)
键值是唯一的,一个键值只能对应一个元素
(2)多重关联容器(multiset和multimap)
键值是不唯一的,一个键值可以对应多个元素
(3)简单关联容器(set和multiset)
(4)二元关联容器(map和multimap)
(5)无序关联容器
集合用来存储一组无重复的元素。由于集合的元素本身是有序的,可以高效地查找指定元素,也可以方便地得到指定大小范围的元素在容器中所处的区间。
举例:输入一串实数,将重复的去掉,取最大和最小者的中值,分别输出小于等于此中值和大于等于此中值的实数
#include <set>
#include <iterator>
#include <utility>
#include <iostream>
using namespace std;
int main() {
set<double> s;
while (true) {
double v;
cin >> v;
if (v == 0) break; //输入0表示结束
//尝试将v插入
pair<set<double>::iterator,bool> r=s.insert(v);
if (!r.second) //如果v已存在,输出提示信息
cout << v << " is duplicated" << endl;
}
//得到第一个元素的迭代器
set<double>::iterator iter1=s.begin();
//得到末尾的迭代器
set<double>::iterator iter2=s.end();
//得到最小和最大元素的中值
double medium=(*iter1 + *(--iter2)) / 2;
//输出小于或等于中值的元素
cout<< "<= medium: ";
copy(s.begin(), s.upper_bound(medium), ostream_iterator<double>(cout, " "));
cout << endl;
//输出大于或等于中值的元素
cout << ">= medium: ";
copy(s.lower_bound(medium), s.end(), ostream_iterator<double>(cout, " "));
cout << endl;
return 0;
}
输出:
1 2.5 5 3.5 5 7 9 2.5 0
5 is duplicated
2.5 is duplicated
<= medium: 1 2.5 3.5 5
>= medium: 5 7 9
举例1:有五门课程,每门都有相应学分,从中选择三门,输出学分总和
#include <iostream>
#include <map>
#include <utility>
using namespace std;
int main() {
map<string, int> courses;
//将课程信息插入courses映射中
courses.insert(make_pair("CSAPP", 3));
courses.insert(make_pair("C++", 2));
courses.insert(make_pair("CSARCH", 4));
courses.insert(make_pair("COMPILER", 4));
courses.insert(make_pair("OS", 5));
int n = 3; //剩下的可选次数
int sum = 0; //学分总和
while (n > 0) {
string name;
cin >> name; //输入课程名称
map<string, int>::iterator iter = courses.find(name);//查找课程
if (iter == courses.end()) { //判断是否找到
cout << name << " is not available" << endl;
} else {
sum += iter->second; //累加学分
courses.erase(iter); //将刚选过的课程从映射中删除
n--;
}
}
cout << "Total credit: " << sum << endl; //输出总学分
return 0;
}
输出:
OS
COMPILER
C++
Total credit: 11
举例2:统计一句话中每个字母出现的次数
#include <iostream>
#include <map>
#include <cctype>
using namespace std;
int main() {
map<char, int> s; //用来存储字母出现次数的映射
char c; //存储输入字符
do {
cin >> c; //输入下一个字符
if (isalpha(c)){ //判断是否是字母:isalpha(c)若为英文字母,返回非0(小写字母为2,大写字母为1)。若不是字母,返回0。
c = tolower(c); //将字母转换为小写
s[c]++; //将该字母的出现频率加1
}
} while (c != '.'); //碰到“.”则结束输入
//输出每个字母出现次数
for (map<char, int>::iterator iter = s.begin(); iter != s.end(); ++iter)
cout << iter->first << ": " << iter->second << " ";
cout << endl;
return 0;
}
输出:
isalpha112a.
a: 3 h: 1 i: 1 l: 1 p: 1 s: 1
一个行为类似函数的对象
可以没有参数,也可以带有若干参数
其功能是获取一个值,或者改变操作的状态。
例:
(1)普通函数就是函数对象
(2)重载了“()”运算符的类的实例是函数对象
比如使用两种方式定义表示乘法的函数对象:
用到的算法:
template<class InputIterator, class Type, class BinaryFunction>
Type accumulate(InputIterator first, InputIterator last, Type val, BinaryFunction binaryOp);
对[first, last)区间内的数据进行累“加”,binaryOp为用二元函数对象表示的“加”运算符,val为累“加”的初值
(1)通过定义普通函数
#include <iostream>
#include <numeric> //包含数值算法头文件
using namespace std;
//定义一个普通函数
int mult(int x, int y) { return x * y; };
int main() {
int a[] = { 1, 2, 3, 4, 5 };
const int N = sizeof(a) / sizeof(int);
cout << "The result by multipling all elements in a is "
<< accumulate(a, a + N, 1, mult) //对a内的数据进行"运算",binaryOp为用二元函数对象表示的某种"运算",val为计算的初值
<< endl;
return 0;
}
输出:The result by multipling all elements in a is 120
(2)通过重载类的“()”运算符(例10-14)
#include <iostream>
#include <numeric> //包含数值算法头文件
using namespace std;
class MultClass{ //定义MultClass类
public:
//重载操作符operator()
int operator() (int x, int y) const { return x * y; }
};
int main() {
int a[] = { 1, 2, 3, 4, 5 };
const int N = sizeof(a) / sizeof(int);
cout << "The result by multipling all elements in a is "
<< accumulate(a, a + N, 1, MultClass()) //将类multclass传递给通用算法
<< endl;
return 0;
}
输出:The result by multipling all elements in a is 120
(1)用于算术运算的函数对象:
一元函数对象(一个参数) :negate
二元函数对象(两个参数) :plus、minus、multiplies、divides、modulus
(2)用于关系运算、逻辑运算的函数对象(要求返回值为bool)
一元谓词(一个参数):logical_not
二元谓词(两个参数):equalto、notequalto、greater、less、greaterequal、lessequal、logicaland、logical_or
举例:利用STL标准函数对象
#include <iostream>
#include <numeric> //包含数值算法头文件
#include <functional> //包含标准函数对象头文件
using namespace std;
int main() {
int a[] = { 1, 2, 3, 4, 5 };
const int N = sizeof(a) / sizeof(int);
cout << "The result by multipling all elements in A is "
<< accumulate(a, a + N, 1, multiplies<int>())//multiplies<int>():二元函数对象(两个参数)
<< endl; //将标准函数对象传递给通用算法
return 0;
}
举例:利用STL中的二元谓词函数对象
#include <functional>
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main() {
int intArr[] = { 30, 90, 10, 40, 70, 50, 20, 80 };
const int N = sizeof(intArr) / sizeof(int);
vector<int> a(intArr, intArr + N);
cout << "before sorting:" << endl;
copy(a.begin(),a.end(),ostream_iterator<int>(cout,"\t"));//输出
cout << endl;
sort(a.begin(), a.end(), greater<int>());//重排序:greater<int>()逆序
cout << "after sorting:" << endl;
copy(a.begin(),a.end(),ostream_iterator<int>(cout,"\t"));//输出
cout << endl;
sort(a.begin(), a.end(), less<int>());//重排序:greater<int>()逆序
cout << "again sorting:" << endl;
copy(a.begin(),a.end(),ostream_iterator<int>(cout,"\t"));//输出
cout << endl;
return 0;
}
输出:
before sorting:
30 90 10 40 70 50 20 80
after sorting:
90 80 70 50 40 30 20 10
again sorting:
10 20 30 40 50 70 80 90
(1)绑定适配器:bind1st、bind2nd
(2)组合适配器:not1、not2
(3)函数指针适配器:ptr_fun
(4)成员函数适配器:ptrfun、ptrfun_ref
举例:找到数组中第一个大于40的元素
算法介绍:
template<class InputIterator, class UnaryPredicate>
InputIterator find_if(InputIterator first, InputIterator last, UnaryPredicate pred);
它的功能是查找数组[first, last)区间中第一个pred(x)为真的元素。
#include <functional>
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main() {
int intArr[] = { 30, 90, 10, 40, 70, 50, 20, 80 };
const int N = sizeof(intArr) / sizeof(int);
vector<int> a(intArr, intArr + N);
vector<int>::iterator p = find_if(a.begin(), a.end(), bind2nd(greater<int>(), 40));
if (p == a.end())
cout << "no element greater than 40" << endl;
else
cout << "first element greater than 40 is: " << *p << endl;
return 0;
}
举例:ptr_fun、not1和not2产生函数适配器实例
#include <functional>
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
bool g(int x, int y) {
return x > y;
}
int main() {
int intArr[] = { 30, 90, 10, 40, 70, 50, 20, 80 };
const int N = sizeof(intArr) / sizeof(int);
vector<int> a(intArr, intArr + N);
vector<int>::iterator p;
p = find_if(a.begin(), a.end(), bind2nd(ptr_fun(g), 40));//找到第一个大于40的数:90
if (p == a.end())
cout << "no element greater than 40" << endl;
else
cout << "first element greater than 40 is: " << *p << endl;
p = find_if(a.begin(), a.end(), not1(bind2nd(greater<int>(), 15)));//not1逻辑(greater)取反,即比12小的
if (p == a.end())
cout << "no element is not greater than 15" << endl;
else
cout << "first element that is not greater than 15 is: " << *p << endl;
p = find_if(a.begin(), a.end(), bind2nd(not2(less<int>()), 15));
if (p == a.end())
cout << "no element is greater than 15" << endl;
else
cout << "first element that is greater than 15 is: " << *p << endl;
return 0;
}
输出:
first element greater than 40 is: 90
first element that is not greater than 15 is: 10
first element that is greater than 15 is: 30
举例:成员函数适配器实例
#include <functional>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct Car {
int id;
Car(int id) { this->id = id; }
void display() const { cout << "car " << id << endl; }
};
int main() {
vector<Car *> pcars;
vector<Car> cars;
for (int i = 0; i < 5; i++)
pcars.push_back(new Car(i));
for (int i = 5; i < 10; i++)
cars.push_back(Car(i));
cout << "elements in pcars: " << endl;
for_each(pcars.begin(), pcars.end(), std::mem_fun(&Car::display));//将成员函数适配(mem_fun)为普通函数对象
cout << endl;
cout << "elements in cars: " << endl;
for_each(cars.begin(), cars.end(), std::mem_fun_ref(&Car::display));//mem_fun_ref的参数是对象的引用;mem_fun的参数是对象的指针
cout << endl;
for (size_t i = 0; i < pcars.size(); ++i)
delete pcars[i];
return 0;
}
输出:
elements in pcars:
car 0
car 1
car 2
car 3
car 4
elements in cars:
car 5
car 6
car 7
car 8
car 9
例如:
template<class InputIterator, class UnaryPredicate>
InputIterator find_if(InputIterator first, InputIterator last, UnaryPredicate pred);
查找[first, last)区间内pred(x)为真的首个元素
例:
template<class ForwardIterator, class T>
InputIterator find_if(ForwardIterator first, ForwardIterator last, const T& x);
将[first, last)区间内的元素全部改写为x。
例如:
template <class RandomAccessIterator , class UnaryPredicate>
void sort(RandomAccessIterator first, RandomAccessIterator last, UnaryPredicate comp);
以函数对象comp为“<”,对 [first, last)区间内的数据进行排序
例:
template<class InputIterator, class OutputIterator, class BinaryFunction>
OutputIterator partial_sum(InputIterator first, InputIterator last, OutputIterator result, BinaryFunction op);
对[first, last)内的元素求部分“和”(所谓部分“和”,是一个长度与输入序列相同的序列,其第n项为输入序列前n个元素的“和”),以函数对象op为“+”运算符,结果通过result输出,返回的迭代器指向输出序列最后一个元素的下一个元素
(1)定义 const int b[3] ={9,2,3};调用insertionSort函数, insertionSort(b,3),可以执行成功。
正确。
分析:const类型不是 Sortable概念的模型
(2)set::insert(const value_type&val); 函数的返回值类型是什么?
iterator
指针
bool
pair<iterator,bool>(对)
(3)
#include
#include
using namespace std;
/*
(4)
#include <iostream>
#include <algorithm> //程序中使用了算法find和sort
#include <functional> //程序中使用了函数对象greater
using namespace std;
int main()
{
int a[8] = {3, 2, 5, 6, 4, 1, 9, 8};
cout << "元素9的索引是:" << find(a, a+8, 9) - a << endl;//find返回的是地址
cout << "升序排序:" << endl;
sort(a, a+8);//默认是升序
for (int i = 0; i < 8; i ++)
cout << a[i] << ' ';
cout << endl;
cout << "升序排序:" << endl;
sort(a, a+8, less<int>());//使用函数对象实现降序排序
for (int i = 0; i < 8; i ++)
cout << a[i] << ' ';
cout << endl;
cout << "降序排序:" << endl;
sort(a, a+8, greater<int>());//使用函数对象实现降序排序
for (int i = 0; i < 8; i ++)
cout << a[i] << ' ';
cout << endl;
return 0;
}
输出:
升序排序:
1 2 3 4 5 6 8 9
升序排序:
1 2 3 4 5 6 8 9
降序排序:
9 8 6 5 4 3 2 1
手机扫一扫
移动阅读更方便
你可能感兴趣的文章