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++ 中有两个方面体现重用:
- 面向对象的思想:继承和多态,标准类库
- 泛型程序设计 (generic programming) 的思想: 模板机制,以及标准模板库 STL
简单地说就是使用模板的程序设计法。
将一些常用的数据结构(比如链表,数组,二叉树)和算法(比如排序,查找)写成模板,以后则不论数据结构里放的是什么对象,算法针对什么样的对象,则都不必重新实现数据结构,重新编写算法。
标准模板库 (Standard Template Library) 就是一些常用数据结构和算法的模板集合 。
有了STL,不必再写大多的标准数据结构和算法,并且可获得非常高的性能。
STL中的基本的概念
容器:可容纳各种数据类型的通用数据结构,是类模板
迭代器:可用于依次存取容器中元素,类似于指针
算法:用来操作容器中的元素的函数模板
- sort()来对一个vector中的数据进行排序
- find()来搜索一个list中的对象
算法本身与他们操作的数据的类型无关,因此他们可以在从简单数组到高度复杂容器的任何数据结构上使用。
int array[100];
该数组就是容器,而int* 类型的指针变量就可以作为迭代器,sort算法可以作用于该容器上,对其进行排序:
sort(array,array+70); //将前70个元素排序
容器概述
可以用于存放各种类型的数据(基本类型的变量,对象等)的数据结构,都是类模版,分为三种:
- 顺序容器
vector,deque,list - 关联容器
set,multiset,map,multimap - 容器适配器
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时,在缺省的情况下,以下三个说法等价:
- x比y小
- 表达式“x<y”为真
- 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,则x比y小。
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() | 成员函数返回适配器中元素个数 |