C++面向对象——标准模板库STL

string类

string类

  • string类是模板类:
 typedef basic_string<char>string;
  • 使用string类要包含头文件<string>

  • string对象的初始化:

 string s1("Hello");
 string month="March";
 string s2(8,'x');
  • 错误的初始化方法:
    –string error1=‘c’; //错
    –string error2(‘u’); //错
    –string error3=22; //错
    –string error4(8); //错
  • 可以将字符赋值给string对象
    –string s;
    –s=‘n’;

string类程序样例

#include<iostream>
#include<string>
using namespace std;
int main(int argc,char*argv[]) {
	string s1("Hello");
	cout << s1 << endl;
	string s2(8, 'x');
	cout << s2 << endl;
	string month = "March";
	cout << month << endl;
	string s;
	s = 'n';
	cout << s << endl;
	return 0;
}
// 输出:
// Hello
// xxxxxxxx
// March
// n

string类

  • string对象的长度用成员函数length()读取;
 string s("hello");
 cout<<s.length()<<endl;
  • string支持流读取运算符
string stringObject;
cin>>stringObject;
  • string支持getline函数
 string s;
 getline(cin,s);

string的赋值

  • 用 = 赋值
 string s1("cat"),s2;
 s2=s1;
  • 用assign成员函数复制
 string s1("cat"),s3;
 s3.assign(s1);
  • 用assign成员函数部分复制
 string s1("catpig"),s3;
 s3.assign(s1,1,3); //从s1中下标为1的字符开始复制3个字
  • 单个字符复制
 s2[5]=s1[3]='a';
  • 逐个访问string对象中的字符
 string s1("Hello");
 for(int i=0;i<s1.length();i++)
 cout<<s1.at(i)<<endl;
  • 成员函数at会做范围检查,如果超出范围,会抛出out_of_range异常,而下标运算符[]不做范围检查。

string的连接

  • 用 + 运算符连接字符串
 string s1("good"),s2("morning!");
 s1+=s2;
 cout<<s1;
  • 用成员函数append连接字符串
string s1("good"),s2("morning!");
s1.append(s2);
cout<<s1;
s2.append(s1,3,s1.size()); //s1.size(),s1字符数
cout<<s2;
//从下标为3开始,s1.size()个字符,如果字符串内没有足够字符,则复制到字符串最后一个字符

比较string

  • 用关系运算符比较string的大小
    – ==, >, >=, <, <=, !=
    – 返回值都是bool类型,成立返回true,否则返回false
    –例如:
string s1("hello"),s2("hello"),s3("hell");
bool b=(s1==s2);
cout<<b<<endl;
b=(s1==s3);
cout<<b<<endl;
  • 用成员函数compare比较string的大小
string s1("hello"), s2("hello"), s3("hell");
int f1 = s1.compare(s2);
int f2 = s1.compare(s3);
int f3 = s3.compare(s1);
int f4 = s1.compare(1,2,s3,0,3);
int f5 = s1.compare(0,s1.size(),s3);
cout << f1 << endl << f2 << endl << f3 << endl;
cout << f4 << endl << f5 << endl;
// 输出:
// 0   //hello==hello
// 1	//hello>hell
// -1	//hell<hello
// -1	//el<hell
// 1	//hello>hell

子串

  • 成员函数substr
string s1("hello world"), s2;
s2 = s1.substr(4, 5); //下标4开始5个字符
cout << s2 << endl;
// 输出:
// o wor

交换string

  • 成员函数swap
string s1("hello world"), s2("really");
s1.swap(s2);
cout << s1 << endl;
cout << s2 << endl;
// 输出:
// really
// hello world

寻找string中的字符

  • 成员函数 find()
 string s1("hello world");
 s1.find("lo");

在s1中从前向后查找 “lo” 第一次出现的地方,如果找到,返回 “lo”开始的位置,即 l 所在的位置下标。如果找不到,返回string::npos (string中定义的静态常量)

  • 成员函数 rfind()
string s1("hello world");
s1.rfind("lo");

在s1中从后向前查找 “lo” 第一次出现的地方,如果找到,返
回 “lo”开始的位置,即 l 所在的位置下标。如果找不到,返回
string::npos 。

  • 成员函数find()
string s1("hello worlld");
cout << s1.find("ll", 1) << endl;
cout << s1.find("ll", 2) << endl;
cout << s1.find("ll", 3) << endl;
// 分别从下标1,2,3开始查找“ll”
// 输出:
// 2
// 2
// 9
  • 成员函数 find_first_of()
string s1("hello world");
s1.find_first_of(“abcd");

在s1中从前向后查找 “abcd” 中任何一个字符第一次出现的地方,如果找到,返回找到字母的位置,如果找不到,返回string::npos。

  • 成员函数 find_last_of()
string s1("hello world"); s1.find_last_of(“abcd");

在s1中查找 “abcd” 中任何一个字符最后一次出现的地方,如果找到,返回找到字母的位置,如果找不到,返回string::npos。

  • 示例:
#include<iostream>
#include<string>
using namespace std;
int main() {
	string s1("hello worlld");
	cout << s1.find("ll") << endl;
	cout << s1.find("abc") << endl;
	cout << s1.rfind("ll") << endl;
	cout << s1.rfind("abc") << endl;
	cout << s1.find_first_of("abcde") << endl;
	cout << s1.find_first_of("abc") << endl;
	cout << s1.find_last_of("abcde") << endl;
	cout << s1.find_last_of("abc") << endl;
	cout << s1.find_first_not_of("abcde") << endl;
	cout << s1.find_first_not_of("hello world") << endl;
	cout << s1.find_last_not_of("abcde") << endl;
	cout << s1.find_last_not_of("hello world") << endl;
	return 0;
}

x86:

输出:
2
4294967295
9
4294967295
1
4294967295
11
4294967295
0
4294967295
10
4294967295

x64:

输出:
2
18446744073709551615
9
18446744073709551615
1
18446744073709551615
11
18446744073709551615
0
18446744073709551615
10
18446744073709551615

删除string中的字符

  • 成员函数erase()
string s1("hello worlld");
s1.erase(5);
cout << s1;
cout << s1.length();
cout << s1.size();
// 去掉下标 5 及之后的字符
// 输出:
// hello55

替换string中的字符

  • 成员函数 replace()
string s1("hello world");
s1.replace(2,3, "haha");
cout << s1;
//将s1中下标2 开始的3个字符换成“haha”
// 输出:
// hehaha world

替换string中的字符

  • 成员函数 replace()
string s1("hello world");
s1.replace(2,3, "haha", 1,2);
cout << s1;
// 将s1中下标2 开始的3个字符换成“haha” 中下标1开始的2个字符
// 输出:
// heah world

在string中插入字符

  • 成员函数insert()
string s1("hello world");
string s2(“show insert");
s1.insert(5,s2); // 将s2插入s1下标5的位置
cout << s1 << endl;
s1.insert(2,s2,5,3);
//将s2中下标5开始的3个字符插入s1下标2的位置
cout << s1 << endl;
// 输出:
// helloshow insert world
// heinslloshow insert world

转换成C语言式char *字符串

  • 成员函数 c_str()
string s1("hello world");
printf("%s\n", s1.c_str());
// s1.c_str() 返回传统的const char * 类型字符串,且该字符串以‘\0’结尾。
// 输出:
// hello world

转换成C语言式char *字符串

  • 成员函数data()
string s1("hello world");
const char * p1=s1.data();
for(int i=0;i<s1.length();i++)
printf("%c",*(p1+i));
//s1.data() 返回一个char * 类型的字符串,对s1 的修改可能会使p1出错。
// 输出:
// hello world

字符串拷贝

  • 成员函数copy()
string s1("hello world");
int len = s1.length();
char * p2 = new char[len+1];
s1.copy(p2,5,0);
p2[5]=0;
cout << p2 << endl;
// s1.copy(p2,5,0) 从s1的下标0的字符开始制作一个最长5个字符长度的字符串副本并将其赋值给p2。返回值表明实际复制字符串的长度。
// 输出:
// hello

字符串流处理

  • 除了标准流和文件流输入输出外,还可以从string进行输入输出;
  • 类似 istream和osteram进行标准流输入输出,我们用istringstream 和 ostringstream进行字符串上的输入输出,也称为内存输入输出。
#include <string>
#include <iostream>
#include <sstream>

字符串流处理 - 字符串输入流 istringstream

string input("Input test 123 4.7 A");
istringstream inputString(input);
string string1, string2;
int i;
double d;
char c;
inputString >> string1 >> string2 >> i >> d >> c;
cout << string1 << endl << string2 << endl;
cout << i << endl << d << endl << c <<endl;
long L;
if(inputString >> L) cout << "long\n";
else cout << "empty\n";
// 输出:
// Input
// test
// 123
// 4.7
// A
// empty

字符串流处理 - 字符串输出流 istringstream

ostringstream outputString;
int a = 10;
outputString << "This " << a << "ok" << endl;
cout << outputString.str();
// 输出:
// This 10ok

标准模板库STL概述

泛型程序设计

C++ 语言的核心优势之一就是便于软件重用

C++ 中有两个方面体现重用:

  1. 面向对象的思想:继承和多态,标准类库
  2. 泛型程序设计 (generic programming) 的思想: 模板机制,以及标准模板库 STL

简单地说就是使用模板的程序设计法。

将一些常用的数据结构(比如链表,数组,二叉树)和算法(比如排序,查找)写成模板,以后则不论数据结构里放的是什么对象,算法针对什么样的对象,则都不必重新实现数据结构,重新编写算法。

标准模板库 (Standard Template Library) 就是一些常用数据结构和算法的模板集合 。

有了STL,不必再写大多的标准数据结构和算法,并且可获得非常高的性能。

STL中的基本的概念

容器:可容纳各种数据类型的通用数据结构,是类模板

迭代器:可用于依次存取容器中元素,类似于指针

算法:用来操作容器中的元素的函数模板

  • sort()来对一个vector中的数据进行排序
  • find()来搜索一个list中的对象

算法本身与他们操作的数据的类型无关,因此他们可以在从简单数组到高度复杂容器的任何数据结构上使用。

int array[100];

该数组就是容器,而int* 类型的指针变量就可以作为迭代器,sort算法可以作用于该容器上,对其进行排序:

sort(array,array+70); //将前70个元素排序

容器概述

可以用于存放各种类型的数据(基本类型的变量,对象等)的数据结构,都是类模版,分为三种:

  1. 顺序容器
    vector,deque,list
  2. 关联容器
    set,multiset,map,multimap
  3. 容器适配器
    stack,queue,priority_queue

对象被插入容器中时,被插入的是对象的一个复制品。许多算法,比如排序,查找,要求对容器中的元素进行比较,有的容器本身就是排序的,所以,放入容器的对象所属的类,往往还应该重载 == 和 < 运算符。

顺序容器简介

容器并非排序的,元素的插入位置同元素的值无关。

有vector,deque,list三种

  • vector
头文件<vector>

动态数组。元素在内存连续存放。随机存取任何元素都能在常数时间完成。在尾端增删元素具有较佳的性能(大部分情况下是常数时间)。
在这里插入图片描述

  • deque
 头文件<deque>

双向队列。元素在内存连续存放。随机存取任何元素都能在常数时间完成(但次于vector)。在两端增删元素具有较佳的性能(大部分情况下是常数时间)。
在这里插入图片描述

  • deque
头文件<deque>

双向队列。元素在内存连续存放。随机存取任何元素都能在常数时间完成(但次于vector)。在两端增删元素具有较佳的性能(大部分情况下是常数时间)。
在这里插入图片描述

  • list
 头文件<list>

双向链表。元素在内存不连续存放。在任何位置增删元素都能在常数时间完成。不支持随机存取。
在这里插入图片描述

关联容器简介

– 元素是排序
– 插入任何元素,都按相应的排序规则来确定其位置
– 在查找时具有非常好的性能
– 通常以平衡二叉树方式实现,插入和检索的时间都是O(log(N))

  • set/multiset
头文件<set>

set即集合。set中不允许相同元素,multiset中允许存在相同的元素。

  • map/multimap
头文件<map>

map与set的不同在于map中存放的元素有且仅有两个成员变量,一个名为first,另一个名为second, map 根据first值对元素进行从小到大排序,并可快速地根据first来检索元素

map同multimap的不同在于是否允许相同first值的元素。

容器适配器简介

  • stack
头文件<stack>

栈。是项的有限序列,并满足序列中被删除、检索和修改的项只能是最近插入序列的项(栈顶的项)。后进先出
在这里插入图片描述

  • queue
头文件<queue>

队列。插入只可以在尾部进行,删除、检索和修改只允许从头部进行。先进先出
在这里插入图片描述

  • priority_queue
头文件<queue>

优先级队列。最高优先级元素总是第一个出列

顺序容器和关联容器中都有的成员函数

begin    	返回指向容器中第一个元素的迭代器
end			返回指向容器中最后一个元素后面的位置的迭代器
rbegin		返回指向容器中最后一个元素的迭代器
rend        返回指向容器中第一个元素前面的位置的迭代器
erase		从容器中删除一个或几个元素
clear		从容器中删除所有元素

顺序容器的常用成员函数

front:返回容器中第一个元素的引用
back:返回容器中最后一个元素的引用
push_back:在容器末尾增加新元素
pop_back:删除容器末尾的元素
erase:删除迭代器指向的元素(可能会使该迭代器失效),或删除一个区间,返回被删除元素后面的那个元素的迭代器

迭代器

  • 用于指向顺序容器和关联容器中的元素
  • 迭代器用法和指针类似
  • 有const和非const两种
  • 通过迭代器可以读取它指向的元素
  • 通过非const迭代器还能修改其指向的元素

定义一个容器类的迭代器的方法可以是:

容器类名::iterator变量名;

或:

容器类名::const_iterator变量名;

访问一个迭代器指向的元素:

*迭代器变量名

迭代器上可以执行++操作,以使其指向容器中的下一个元素。如果迭代器到达了容器中的最后一个元素的后面,此时再使用它,就会出错,类似于使用NULL或未初始化的指针一样。

#include <vector>
#include <iostream>
using namespace std;
int main() {
	vector<int> v; //一个存放int元素的数组,一开始里面没有元素
	v.push_back(1); 
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	vector<int>::const_iterator i; //常量迭代器
	for (i = v.begin(); i != v.end(); ++i)
		cout << *i << ",";
	cout << endl;
	vector<int>::reverse_iterator r;//反向迭代器
	for (r = v.rbegin(); r != v.rend(); r++)
		cout << *r << ",";
	cout << endl;
	vector<int>::iterator j;//非常量迭代器
	for (j = v.begin(); j != v.end(); j++)
		*j = 100;
	for (i = v.begin(); i != v.end(); i++)
		cout << *i << ",";
	return 0;
}

输出结果:
1,2,3,4,
4,3,2,1,
100,100,100,100

双向迭代器

若p和p1都是双向迭代器,则可对p、p1可进行以下操作:

++p, p++ 			使p指向容器中下一个元素
--p, p--			使p指向容器中上一个元素
*p					取p指向的元素
p = p1				赋值
p == p1 , p!= p1	判断是否相等、不等

随机访问迭代器

若p和p1都是随机访问迭代器,则可对p、p1可进行以下操作:

  • 双向迭代器的所有操作
  • p +=i将p向后移动i个元素
  • p-=i将p向向前移动i个元素
  • p +i值为:指向p后面的第i个元素的迭代器
  • p-i值为:指向p前面的第i个元素的迭代器
  • p[i]值为: p后面的第i个元素的引用
  • p < p1, p <= p1, p > p1,p>= p1
  • p–p1 : p1和p之间的元素个数
容器容器上的迭代器类别
vector随机访问
deque随机访问
list双向
set/multiset双向
map/multimap双向
stack不支持迭代器
queuepriority_queue不支持迭代器
priority_queue不支持迭代器

有的算法,例如sort,binary_search需要通过随机访问迭代器来访问容器中的元素,那么list以及关联容器就不支持该算法!

vector的迭代器是随机迭代器,遍历vector可以有以下几种做法(deque亦然):

vector<int> v(100);
int i;
for(i = 0;i < v.size() ; i ++)
cout <<v[i];//根据下标随机访问
vector<int>::const_iterator ii;
for( ii = v.begin(); ii!=v.end ();++ii)
cout << * ii;
for( ii = v.begin(); ii<v.end ();++ii )
cout << * ii;
//间隔一个输出:
ii =v.begin();
while( ii <v.end()) {
cout<< * ii;
ii = ii + 2;
}

list的迭代器是双向迭代器,正确的遍历list的方法:

list<int> v;
list<int>::const_iterator ii;
for( ii = v.begin(); ii!=v.end ();++ii )
cout << * ii;

错误的做法:

for( ii =v.begin(); ii<v.end();++ii )
cout<< * ii;
//双向迭代器不支持<,list没有[]成员函数
for(inti= 0;i <v.size() ;i++)
cout<< v[i];

算法简介

  • 算法就是一个个函数模板,大多数在<algorithm>中定义
  • STL中提供能在各种容器中通用的算法,比如查找,排序等
  • 算法通过迭代器来操纵容器中的元素。许多算法可以对容器中的一个局部区间进行操作,因此需要两个参数,一个是起始元素的迭代器,一个是终止元素的后面一个元素的迭代器。比如,排序和查找
  • 有的算法返回一个迭代器。比如find()算法,在容器中查找一个元素,并返回一个指向该元素的迭代器
  • 算法可以处理容器,也可以处理普通数组

算法示例:find()

template<class init,class T>
init find(init first, init last, const T& val);
  • first和last这两个参数都是容器的迭代器,它们给出了容器中的查找区间起点和终点[first,last)。区间的起点是位于查找范围之中的,而终点不是。find在[first,last)查找等于val的元素
  • 用 == 运算符判断相等
  • 函数返回值是一个迭代器。如果找到,则该迭代器指向被找到的元素;如果找不到,则该迭代器等于last
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
int main() {		//find算法示例
	int array[10] = { 10,20,30,40 };
	vector<int> v;
	v.push_back(1);		v.push_back(2);
	v.push_back(3);		v.push_back(4);
	vector<int>::iterator p;
	p = find(v.begin(), v.end(), 3);
	if (p != v.end())
		cout << *p << endl;   //输出 3
	p = find(v.begin(), v.end(), 9);
	if (p == v.end())
		cout << "not found" << endl;
	p = find(v.begin() + 1, v.end() - 2, 1);
	//整个容器:[1,2,3,4],查找区间:[2,3)
	if (p != v.end())
		cout << *p << endl;
	int* pp = find(array, array + 4, 20); //数组名是迭代器
	cout << *pp << endl;
	return 0;
}
// 输出:
// 3
// not found
// 3
// 20

STL中“大”“小”的概念

  • 关联容器内部的元素是从小到大排序的
  • 有些算法要求其操作的区间是从小到大排序的,称为“有序区间算法
    例:binary_search
  • 有些算法会对区间进行从小到大排序,称为“排序算法
    例:sort
  • 还有一些其他算法会用到“大”,“小”的概念

使用STL时,在缺省的情况下,以下三个说法等价:

  1. x比y小
  2. 表达式“x<y”为真
  3. y比x大

STL中“相等”的概念

  • 有时,“x和y相等”等价于“x==y为真
    例:在未排序的区间上进行的算法,如顺序查找find
    ……
  • 有时“x和y相等”等价于“x小于y和y小于x同时为假
    例:有序区间算法,如binary_search关联容器自身的成员函数find
    ……

STL中“相等”概念演示

#include<iostream>
#include<algorithm>
using namespace std;
class A {
	int v;
public:
	A(int n):v(n){ }
	bool operator < (const A & a2)const {
		//必须为常量成员函数
		cout << v << "<" << a2.v << "?" << endl;
		return false;
	}
	bool operator==(const A& a2)const {
		cout << v << "==" << a2.v << "?" << endl;
		return v == a2.v;
	}
};
int main() {
	A a[] = { A(1),A(2),A(3),A(4),A(5) };
	cout << binary_search(a, a + 4, A(9));
	return 0;
}
输出结果:
3<9?
2<9?
1<9?
9<1?
1

vector

vector示例程序

#include<iostream>
#include<vector>
using namespace std;
template<class T>
void PrintVector(T s, T e) {
	for (; s != e; ++s)
		cout << *s << " ";
	cout << endl;
}
int main() {
	int a[5] = { 1,2,3,4,5 };
	vector<int> v(a, a + 5);  //将数组a的内容放入v
	cout << "1)" << v.end() - v.begin() << endl;
	//两个随机迭代器可以相减,输出1) 5
	cout << "2)"; PrintVector(v.begin(), v.end());
	//2) 1 2 3 4 5
	v.insert(v.begin() + 2, 13);	//在begin()+2位置插入13
	cout << "3)"; PrintVector(v.begin(), v.end());
	//3) 1 2 13 3 4 5
	v.erase(v.begin() + 2);		//删除位于begin() + 2的元素
	cout << "4)"; PrintVector(v.begin(), v.end());
	//4) 1 2 3 4 5
	vector<int>v2(4, 100);	 //v2有4个元素,都是100
	v2.insert(v2.begin(), v.begin() + 1, v.begin() + 3);
	//将v的一段插入v2开头
	cout << "5) v2: "; PrintVector(v2.begin(), v2.end());
	//5) v2: 2 3 100 100 100 100
	v.erase(v.begin() + 1, v.begin() + 3);
	//删除v上的一个区间,即2,3
	cout << "6) "; PrintVector(v.begin(), v.end());
	//6) 1 4 5
	return 0;
}
1)5
2)1 2 3 4 5
3)1 2 13 3 4 5
4)1 2 3 4 5
5) v2: 2 3 100 100 100 100
6) 1 4 5

用vector实现二维数组

#include <iostream>
#include <vector>
using namespace std;
int main() {
	vector<vector<int>>v(3);
	for (int i = 0; i < v.size(); ++i)
		for (int j = 0; j < 4; ++j)
			v[i].push_back(j);
	for (int i = 0; i < v.size(); ++i) {
		for (int j = 0; j < v[i].size(); ++j)
			cout << v[i][j] << " ";
		cout << endl;
	}
	return 0;
}
程序输出结果:
0 1 2 3
0 1 2 3
0 1 2 3

deque

所有适用于vector的操作都适用于deque

deque还有push_front(将元素插入到前面)和pop_front(删除最前面的元素)操作,复杂度是O(1)

双向链表list

list容器

在任何位置插入删除都是常数时间,不支持随机存取。

除了具有所有顺序容器都有的成员函数以外,还支持8个成员函数:

  • push_front:在前面插入
  • pop_front:删除前面的元素
  • sort:排序( list不支持STL的算法sort)
  • remove:删除和指定值相等的所有元素
  • unique:删除所有和前一个元素相同的元素(要做到元素不重复,则unique之前还需要sort)
  • merge:合并两个链表,并清空被合并的那个
  • reverse:颠倒链表
  • splice:在指定位置前面插入另一链表中的一个或多个元素,并在另一链表中删除被插入的元素
#include<list>
#include <iostream>
#include <algorithm>
using namespace std;
class A {
private:
	int n;
public:
	A(int n_) { n = n_; }
	friend bool operator<(const A& a1, const A& a2);
	friend bool operator==(const A& a1, const A& a2);
	friend ostream& operator<<(ostream& o, const A& a);
};
bool operator<(const A& a1, const A& a2) {
	return a1.n < a2.n;
}
bool operator==(const A& a1, const A& a2) {
	return a1.n == a2.n;
}
ostream& operator <<(ostream& o, const A& a) {
	o << a.n;
	return o;
}
template <class T>
void PrintList(const list<T>& lst) {
	//不推荐的写法,还是用两个迭代器作为参数更好
	int tmp = lst.size();
	if (tmp > 0) {
		typename list<T>::const_iterator i;
		for (i = lst.begin(); i != lst.end(); i++)
			cout << *i << ",";
	}
}// typename用来说明list<T>::const_iterator是个类型
//在vs中不写也可以
int main() {
	list<A>  lst1, lst2;
	lst1.push_back(1); lst1.push_back(3);
	lst1.push_back(2); lst1.push_back(4);
	lst1.push_back(2);
	lst2.push_back(10); lst2.push_front(20);
	lst2.push_back(30); lst2.push_back(30);
	lst2.push_back(30); lst2.push_front(40);
	lst2.push_back(40);
	cout << "1) "; PrintList(lst1); cout << endl;
	// 1) 1,3,2,4,2,
	cout << "2) "; PrintList(lst2); cout << endl;
	// 2) 40,20,10,30,30,30,40,
	lst2.sort();
	cout << "3) "; PrintList(lst2); cout << endl;
	//3) 10,20,30,30,30,40,40,
	lst2.pop_front();
	cout << "4) "; PrintList(lst2); cout << endl;
	//4) 20,30,30,30,40,40,
	lst1.remove(2);//删除所有和A(2)相等的元素
	cout << "5) "; PrintList(lst1); cout << endl;
	//5) 1,3,4,
	lst2.unique();//删除所有和前一个元素相等的元素
	cout << "6) "; PrintList(lst2); cout << endl;
	//6) 20,30,40,
	lst1.merge(lst2);//合并lst2到lst1并清空lst2
	cout << "7) "; PrintList(lst1); cout << endl;
	//7) 1,3,4,20,30,40,
	cout << "8) "; PrintList(lst2); cout << endl;
	//8)
	lst1.reverse();
	cout << "9) "; PrintList(lst1); cout << endl;
	//9) 40,30,20,4,3,1,
	lst2.push_back(100); lst2.push_back(200);
	lst2.push_back(300); lst2.push_back(400);
	list<A>::iterator p1, p2, p3;
	p1 = find(lst1.begin(), lst1.end(), 3);
	p2 = find(lst2.begin(), lst2.end(), 200);
	p3 = find(lst2.begin(), lst2.end(), 400);
	lst1.splice(p1, lst2, p2, p3);
	//将[p2,p3)插入p1之前,并从lst2中删除[p2,p3)
	cout << "10) "; PrintList(lst1); cout << endl;
	//10) 40,30,20,4,200,300,3,1,
	cout << "11) "; PrintList(lst2); cout << endl;
	//11) 100,400,
	return 0;
}
输出结果:
1) 1,3,2,4,2,
2) 40,20,10,30,30,30,40,
3) 10,20,30,30,30,40,40,
4) 20,30,30,30,40,40,
5) 1,3,4,
6) 20,30,40,
7) 1,3,4,20,30,40,
8)
9) 40,30,20,4,3,1,
10) 40,30,20,4,200,300,3,1,
11) 100,400,

函数对象

函数对象

是个对象,但是用起来看上去象函数调用,实际上也执行了函数调用。

class CMyAverage {
public:
	double operator()(int a1, int a2, int a3) {
		//重载()运算符
		return (double)(a1 + a2 + a3) / 3;
	}
};
CMyAverage average;   //函数对象
cout << average(3, 2, 3); //average.operator()(3,2,3)用起来看上去像函数调用 
							//输出 2.66667

函数对象的应用:

STL里有以下模板:

template<classInIt, class T, classPred>
T accumulate(InItfirst,InItlast, Tval,Predpr);
  • pr就是个函数对象。对 [ first, last ) 中的每个迭代器,执行val= pr(val,* I),返回最终的val。
  • pr也可以是个函数。

Dev C++中的Accumulate源代码1:

template<typename _InputIterator, typename _Tp>
_Tp  accumulate(_InputIterator __first, _InputIterator
__last,
_Tp __init)
{
for ( ; __first != __last; ++__first)
__init = __init + *__first;
return __init;
}
// typename等效于class

Dev C++中的Accumulate源代码2:

template<typename _InputIterator, typename _Tp,
typename _BinaryOperation>
_Tp    accumulate(_InputIterator __first, _InputIterator __last,
_Tp __init,     _BinaryOperation __binary_op)
{
for ( ; __first != __last; ++__first)
__init = __binary_op(__init, *__first);
return __init;
}

示例:

#include<iostream>
#include<vector>
#include<algorithm>
#include<numeric>
#include<functional>
using namespace std;
int SumSquares(int total, int value){
	return total + value * value;
}
template<class T>
void PrintInterval(T first, T last) {
	//输出区间[first,last)中的元素
	for (; first != last; ++first)
		cout << *first << " ";
	cout << endl;
}
template<class T>
class SumPowers {
private:
	int power;
public:
	SumPowers(int p) :power(p) { }
	const T operator()(const T& total, const T& value) {
		//计算value的power次方,加到total上
		T v = value;
		for (int i = 0; i < power - 1; ++i)
			v = v * value;
		return total + v;
	}
};
int main() {
	const int SIZE = 10;
	int a1[] = { 1,2,3,4,5,6,7,8,9,10 };
	vector<int> v(a1, a1 + SIZE);
	cout << "1) "; PrintInterval(v.begin(), v.end());
	int result = accumulate(v.begin(), v.end(), 0, SumSquares);
	cout << "2)平方和:" << result << endl; result =
		accumulate(v.begin(), v.end(), 0, SumPowers<int>(3)); cout << "3)立方和:" << result << endl;
	result =
		accumulate(v.begin(), v.end(), 0, SumPowers<int>(4));
	cout << "4) 4次方和:" << result;
	return 0;
}
1) 1 2 3 4 5 6 7 8 9 10
2)平方和:385
3)立方和:3025
4) 4次方和:25333
int result =
accumulate(v.begin(), v.end(), 0, SumSquares);
实例化出:
int accumulate(vector<int>::iterator
	first, vector<int>::iterator last,
	int init, int (*op)(int, int)){
	for (; first != last; ++first)
		init = op(init, *first);
	return init;
}
accumulate(v.begin(), v.end(), 0, SumPowers<int>(3));
实例化出:
int accumulate(vector<int>::iterator first,
	vector<int>::iterator last,
	int init, SumPowers<int> op){
	for (; first != last; ++first)
		init = op(init, *first);
	return init;
}

STL的<functional>里还有以下函数对象类模板:
equal_to
greater
less…….
这些模板可以用来生成函数对象。

greater函数对象类模板

template<class T>
structgreater : publicbinary_function<T, T, bool>{
booloperator()(const T & x, const T & y) const {
return x > y;
}
};
binary_function定义:
template<class Arg1, class Arg2, class Result>
struct binary_function {
	typedef Arg1 first_argument_type;
	typedef Arg2 second_argument_type;
	typedef Result result_type;
};

greater的应用

  • list有两个sort函数,前面例子中看到的是不带参数的sort函数,它将list中的元素按<规定的比较方法升序排列。
  • list还有另一个sort函数:
template <class T2> 
  void sort(T2 op);

可以用op来比较大小,即op(x,y)为true则认为x应该排在前面。

示例:

#include<list>
#include<iostream>
#include<iterator>
using namespace std;
class MyLess {
public:
	bool operator()(const int& c1, const int& c2) {
		return(c1 % 10) < (c2 % 10);
	}
};
int main() {
	const int SIZE = 5;
	int a[SIZE] = { 5,21,14,2,3 };
	list<int>lst(a, a + SIZE);
	lst.sort(MyLess());
	ostream_iterator<int>output(cout, ",");
	copy(lst.begin(), lst.end(), output); 
	cout << endl;
	lst.sort(greater<int>());	//greater<int>()是个对象
								//本句进行降序排序
	copy(lst.begin(), lst.end(), output);
	cout << endl;
	return 0;
}
输出:
21,2,3,14,5,
21,14,5,3,2,

ostream_iterator

copy类似于

template <class T1,class T2>
void Copy(T1 s,T1 e, T2 x)
{
for(; s != e; ++s,++x)
*x = *s;
}

ostream_iterator如何编写?

#include<iostream>
#include<iterator>
using namespace std;
class MyLess {
public:
	bool operator()(int a1,int a2) {
		if ((a1 % 10) < (a2 % 10))
			return true;
		else
			return false;
	}
};
bool MyCompare(int a1, int a2) {
	if ((a1 % 10) < (a2 % 10))
		return false;
	else
		return true;
}
template <class T, class Pred>
T MyMax(T* p, int n, Pred myless)
{
	T tmpmax = p[0];
	for (int i = 1; i < n; i++)
		if (myless(tmpmax, p[i]))
			tmpmax = p[i];
	return tmpmax;
};
int main() {
	int a[] = { 35,7,13,19,12 };
	cout << MyMax(a, 5, MyLess()) << endl;
	cout << MyMax(a, 5, MyCompare) << endl;
	return 0;
}

引入函数对象后,STL中的“大”,“小”关系

关联容器和STL中许多算法,都是可以自定义比较器的。在自定义了比较器op的情况下,以下三种说法是等价的:

  • x小于y
  • op(x,y)返回值为true
  • y大于x

比较规则的注意事项

struct结构名{
	bool operator()(constT& a1,constT& a2) {
	//若a1应该在a2前面,则返回true。//否则返回false。
	}
};

排序规则返回true,意味着a1必须在a2前面
返回false,意味着a1并非必须在a2前面

排序规则的写法,不能造成比较a1,a2返回true比较a2,a1也返回true

否则sort会runtime error

比较a1,a2返回false比较a2,a1也返回false,则没有问题

set和multiset

关联容器

set,multiset,map,multimap

  • 内部元素有序排列,新元素插入的位置取决于它的值,查找速度快。
  • 除了各容器都有的函数外,还支持以下成员函数:
    find:查找等于某个值的元素(x小于y和y小于x同时不成立即为相等)
    lower_bound:查找某个下界
    upper_bound:查找某个上界
    equal_range:同时查找上界和下界
    count:计算等于某个值的元素个数(x小于y和y小于x同时不成立即为相等)
    insert:用以插入一个元素或一个区间

预备知识:pair模板

template<class _T1,class _T2>
struct pair {
	typedef _T1 first_type;
	typedef _T2 second_type;
	_T1 first;
	_T2 second;
	pair():first(),second(){ }
	pair(const _T1&_a,const _T2&_b) :first(_a), second(_b) { }
	template<class _U1,class _U2>
	pair(const pair<_U1,_U2>&_p) :first(_p.first), second(_p.second) { }
};

map / multimap容器里放着的都是pair模板类的对象,且按first从小到大排序。

第三个构造函数用法示例:

pair<int, int>
p(pair<double, double>(5.5, 4.6)); //函数对象的应用p(...)
// p.first = 5, p.second = 4;

multiset

template<class Key,class Pred=less<Key>,class A=allocator<Key>>
//第三个参数可忽略
class multiset{ ......};
  • Pred类型的变量决定了multiset中的元素,“一个比另一个小”是怎么定义的。
    multiset运行过程中,比较两个元素x,y的大小的做法,就是生成一个Pred类型的变量,假定为op,若表达式op(x,y)返回值为true,则xy小。
    Pred的缺省类型是less<Key>
  • less模板的定义:
template<class T>
struct less :public binary_function<T, T, bool> {
	bool operator()(const T& x, const T& y) { return x < y; }const;
}; //less模板是靠<来比大小的
multiset的成员函数
iterator find(constT &val);
在容器中查找值为val的元素,返回其迭代器。如果找不到,返回end()。
iterator insert(constT &val);
将val插入到容器中并返回其迭代器。
void insert( iterator first,iterator last);
将区间[first,last)插入容器。
int count(constT &val);
统计有多少个元素的值和val相等。
iterator lower_bound(constT &val);
查找一个最大的位置it,使得[begin(),it)中所有的元素都比val小。
iterator upper_bound(constT &val);
查找一个最小的位置it,使得[it,end())中所有的元素都比val大。
pair<iterator,iterator> equal_range(constT &val);
同时求得lower_bound和upper_bound。
iterator erase(iterator it);
删除it指向的元素,返回其后面的元素的迭代器(Visual studio 2010上如此,但是在C++标准和Dev C++中,返回值不是这样)。

multiset 的用法

#include<iostream>
#include<set> //使用multiset须包含此文件
using namespace std;
template<class T>
void Print(T first, T last) {
	for (; first != last; ++first)
		cout << *first << " ";
	cout << endl;
}
class A {
private:
	int n;
public:
	A(int n_) { n = n_; }
	friend bool operator<(const A& a1, const A& a2) { return a1.n < a2.n; }
	friend ostream& operator<<(ostream& o, const A& a2) { o << a2.n; return o; }
	friend class MyLess;
};
struct MyLess {
bool operator()(const A&a1,const A&a2){ //按个位数比大小
	return(a1.n % 10) < (a2.n % 10);
}
};
typedef multiset<A>MSET1; //MSET1用"<"比较大小
typedef multiset<A,MyLess>MSET2; //MSET2用MyLess::operator()比较大小
int main() {
	const int SIZE = 6;
	A a[SIZE] = { 4,22,19,8,33,40 };
	MSET1 m1;
	m1.insert(a, a + SIZE);
	m1.insert(22);
	cout << "1)" << m1.count(22) << endl;  //输出 1)2
	cout << "2)"; Print(m1.begin(), m1.end()); //输出 2)4 8 19 22 22 33 40
	//m1元素:4 8 19 22 22 33 40
	MSET1::iterator pp = m1.find(19);
	if (pp != m1.end()) //条件为真说明找到
		cout << "found" << endl;
		//本行会被执行,输出found
	cout << "3)"; cout << *m1.lower_bound(22) << "," << *m1.upper_bound(22) << endl;
	//输出 3) 22,33
	pp = m1.erase(m1.lower_bound(22), m1.upper_bound(22));
	//pp指向被删元素的下一个元素
	cout << "4)"; Print(m1.begin(), m1.end()); //输出 4)4 8 19 33 40
	cout << "5)"; cout << *pp << endl;	//输出 5)33
	MSET2 m2; // m2里的元素按n的个位数从小到大排
	m2.insert(a, a + SIZE);
	cout << "6)"; Print(m2.begin(), m2.end()); //输出6) 40 22 33 4 8 19
	return 0;
}
输出:
1) 2
2) 4 8 19 22 22 33 40
3) 22,33
4) 4 8 19 33 40
5) 33
6) 40 22 33 4 8 19
iterator lower_bound(constT& val); 
查找一个最大的位置it, 使得[begin(), it)中所有的元素都比val小。

set

template<class Key,class Pred=less<Key>,class A=allocator<Key>>
class set{...}
插入set中已有的元素时,忽略插入(即不能重复)
(重复:a不小于b且b不小于a)

set用法示例

#include<iostream>
#include<set>
using namespace std;
int main() {
	typedef set<int>::iterator IT;
	int a[5] = { 3,4,6,1,2 };
	set<int>st(a, a + 5);  //st里是 1 2 3 4 6
	pair<IT, bool>result;
	result = st.insert(5); //st变成 1 2 3 4 5 6
	if (result.second)	//插入成功则输出被插入元素
		cout << *result.first << " inserted" << endl; //输出:5 inserted
	if (st.insert(5).second)cout << *result.first << endl;
	else
		cout << *result.first << " already exists" << endl; //输出 5 already exists
	pair<IT, IT>bounds = st.equal_range(4);
	cout << *bounds.first << "," << *bounds.second;	  //输出:4,5
	return 0;
}
输出结果:
5 inserted
5 already exists
4, 5

map和multimap

multimap

template<class Key,class T,class Pred=less<Key>,class A=allocator<T>>
Key 是first的类型,T是second的类型
class multimap{
...
typedef pair<const Key,T>value_type;
......
};	  //Key 代表关键字的类型

multimap中的元素由<关键字,值>组成,每一个元素是一个pair对象,关键字就是first成员变量,其类型是Key

multimap中允许多个元素的关键字相同。元素按照first成员变量从小到大排列,缺省情况下用less<Key>定义关键字的“小于”关系。

#include <iostream>
#include<map>
using namespace std;
int main(){
	typedef multimap<int, double, less<int>>mmid;
	mmid pairs;
	cout << "1)" << pairs.count(15) << endl;
	pairs.insert(mmid::value_type(15, 2.7)); //typedef pair<const Key,T>value_type;
	pairs.insert(mmid::value_type(15, 99.3));
	cout << "2)" << pairs.count(15) << endl; //求关键字等于某值的元素个数
	pairs.insert(mmid::value_type(30, 111.11));
	pairs.insert(mmid::value_type(10, 22.22));
	pairs.insert(mmid::value_type(25, 33.333));
	pairs.insert(mmid::value_type(20, 9.3));
	for (mmid::const_iterator i = pairs.begin(); i != pairs.end(); i++)
		cout << "(" << i->first << "," << i->second << ")" << ",";
	return 0;
}
1)0
2)2
(10,22.22),(15,2.7),(15,99.3),(20,9.3),(25,33.333),(30,111.11),
//按first从小到大来排

multimap例题

一个学生成绩录入和查询系统,
接受以下两种输入:

Add  name id score
Query score

name是个字符串,中间没有空格,代表学生姓名。id是个整数,代表学号。score是个整数,表示分数。学号不会重复,分数和姓名都可能重复。

两种输入交替出现。第一种输入表示要添加一个学生的信息,碰到这种输入,就记下学生的姓名、id和分数。第二种输入表示要查询,碰到这种输入,就输出已有记录中分数比score低的最高分获得者的姓名、学号和分数。如果有多个学生都满足条件,就输出学号最大的那个学生的信息。如果找不到满足条件的学生,则输出“Nobody”

输入样例:
Add Jack 12 78
Query 78
Query 81
Add Percy 9 81
Add Marry 8 81
Query 82
Add Tom 11 79
Query 80
Query 81

输出样例:
Nobody
Jack 12 78
Percy 9 81
Tom 11 79
Tom 11 79

#include<iostream>
#include<map>
#include<string>
using namespace std;
class CStudent {
public:
	struct CInfo { //类的内部还可以定义类
		int id;
		string name;
	};
	int score;
	CInfo info; //学生的其他信息
};
typedef multimap<int, CStudent::CInfo>MAP_STD;
int main() {
	MAP_STD mp;
	CStudent st;
	string cmd;
	while (cin >> cmd) {
		if (cmd == "Add") {
			cin >> st.info.name >> st.info.id >> st.score;
			mp.insert(MAP_STD::value_type(st.score, st.info));
		}
		else if (cmd == "Query") {
			int score;
			cin >> score;
			MAP_STD::iterator p = mp.lower_bound(score);
			if (p != mp.begin()) {
				--p;
				score = p->first; //比要查询分数低的最高分
				MAP_STD::iterator maxp = p;
				int maxid = p->second.id;
				for (; p != mp.begin() && p->first == score; --p) {
					//遍历所有成绩和score相等的学生
					if (p->second.id > maxid) {
						maxp = p;
						maxid = p->second.id;
					}
				}
				if (p->first == score) {
					//如果上面循环是因为p==mp.begin()
					//而终止,则p指向的元素还要处理
					if (p->second.id > maxid) {
						maxp = p;
						maxid = p->second.id;
					}
				}
				cout << maxp->second.name << " " << maxp->second.id << " " << maxp->first << endl;
			}
			else
				//lower_bound的结果就是begin,说明没人分数比查询分数低
				cout << "Nobody" << endl;
		}
	}
	return 0;
}
mp.insert(MAP_STD::value_type(st.score, st.info));
//mp.insert(make_pair(st.score,st.info));也可以

map

template<class Key,class T,class Pred=less<Key>,class A=allocator<T>>
class map {
	...
		typedef pair<const Key, T>value type;
	......
};

map 中的元素都是pair模板类对象。关键字(first成员变量)各不相同。元素按照关键字从小到大排列,缺省情况下用less<Key>,即 “<” 定义 “小于” 。

map的[ ]成员函数

若pairs为map模板类的对象,

pairs[Key]

返回对关键字等于Key的元素的值(second成员变量)的引用。若没有关键字为Key的元素,则会往pairs里插入一个关键字为Key的元素,其值用无参构造函数初始化,并返回其值的引用。

如:

map<int,double>pairs;

pairs[50]=5;会修改pairs中关键字为50的元素,使其值变成5。

若不存在关键字等于50的元素,则插入此元素,并使其值变为5。

#include<iostream>
#include<map>
using namespace std;
template<class Key,class Value>
ostream& operator<<(ostream& o, const pair<Key, Value>& p) {
	o << "(" << p.first << "," << p.second << ")";
	return o;
}
int main() {
	typedef map<int, double, less<int>>mmid;
	mmid pairs;
	cout << "1)" << pairs.count(15) << endl;
	pairs.insert(mmid::value_type(15, 2.7));
	pairs.insert(make_pair(15, 99.3)); //make_pair生成一个pair对象
	cout << "2)" << pairs.count(15) << endl;
	pairs.insert(mmid::value_type(20, 9.3));
	mmid::iterator i;
	cout << "3)";
	for (i = pairs.begin(); i != pairs.end(); i++)
		cout << *i << ",";
	cout << endl;
	cout << "4)";
	int n = pairs[40]; //如果没有关键字为40的元素,则插入一个
	for (i = pairs.begin(); i != pairs.end(); i++)
		cout << *i << ",";
	cout << endl;
	cout << "5)";
	pairs[15] = 6.28; //把关键字为15的元素值改成6.28
	for (i = pairs.begin(); i != pairs.end(); i++)
		cout << *i << ",";
}
1)0
2)1
3)(15,2.7),(20,9.3),
4)(15,2.7),(20,9.3),(40,0),
5)(15,6.28),(20,9.3),(40,0),

容器适配器

stack

  • stack是后进先出的数据结构,只能插入,删除,访问栈顶的元素。
  • 可用vector, list,deque来实现。缺省情况下,用deque实现。用vector和deque实现,比用list实现性能好。
template<class T, class Cont =deque<T> >
class stack {};
stack上可以进行以下操作:注释
push插入元素
pop弹出元素
top返回栈顶元素的引用

queue

  • 和stack基本类似,可以用list和deque实现。缺省情况下用deque实现。
template<class T, class Cont =deque<T> >
class queue {……
};
  • 同样也有push, pop, top函数。
    但是push发生在队尾;pop, top发生在队头。先进先出。
  • 有back成员函数可以返回队尾元素的引用。

priority_queue

template <class T, class Container = vector<T>,class Compare = less<T> >
classpriority_queue;
  • 和queue类似,可以用vector和deque实现。缺省情况下用vector实现。

  • priority_queue通常用堆排序技术实现,保证最大的元素总是在最前面。即执行pop操作时,删除的是最大的元素;执行top操作时,返回的是最大元素的引用。默认的元素比较器是less<T>

  • push、pop时间复杂度O(logn)

  • top()时间复杂度O(1)

示例:

#include <queue>
#include <iostream>
using namespace std;
intmain()
{
priority_queue<double> pq1;
pq1.push(3.2); pq1.push(9.8); pq1.push(9.8); pq1.push(5.4);
while( !pq1.empty() ) {
cout<<pq1.top() << " ";
pq1.pop();
}//上面输出9.8 9.8 5.4 3.2
cout << endl;
priority_queue<double,vector<double>,greater<double>> pq2;
pq2.push(3.2); pq2.push(9.8); pq2.push(9.8); pq2.push(5.4);
while( !pq2.empty() ) {
cout <<pq2.top() << " ";
pq2.pop();
}
//上面输出3.2 5.4 9.8 9.8
return 0;
}

容器适配器的元素个数

  • stack,queue,priority_queue都有
操作注释
empty()成员函数用于判断适配器是否为空
size()成员函数返回适配器中元素个数
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Driver.SHAO

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值