文章目录
-
标准模板库STL(一)
• C++中有两个方面体现重用:
1.面向对象的思想:继承和多态,标准类库
2.泛型程序设计(generic programming) 的思想: 模板机制,以及标准模板库 STL(有了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)。在两端增删元素具有较佳的性能(大部分情况下是常数间,具有头尾指针。)
•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或未初始化的指针一样。
/* 迭代器示例 输出结果: 1,2,3,4, 4,3,2,1, 100,100,100,10 0, 27 */ #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 << ","; }
-
双向迭代器
• 若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 // 不支持迭代器 queue // 不支持迭代器 priority_queue // 不支持迭代器
// 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(int i = 0;i < v.size() ; i ++) cout << v[i];
但是随即访问迭代器支持[ ] 和 <
-
算法
• 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 <vector> #include <algorithm> #include <iostream> using namespace std; // find算法示例 int main() { 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; system("pause"); return 0; } /* 输出: 3 not found 3 20 */
-
STL 中的“大” “小” “相等” 的概念(自定义比较器)
关联容器内部的元素是从小到大排序的
有些算法要求其操作的区间是从小到大排序的,称为“有序区间算法”
例:binary_search 有些算法会对区间进行从小到大排序,称为“排序算法”
例: sort 还有一些其他算法会用到“大”,“小”的概念
使用STL时,在缺省的情况下,以下三个说法等价:
1)x比y小
2)表达式“x<y”为真
3)y比x大 -
用vector实现二维数组
#include <iostream> #include <vector> using namespace std; int main() { vector<vector<int> > v(3); //v有3个元素,每个元素都是vector<int> 容器 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; } system("pause"); return 0; }
所有适用于 vector的操作都适用于 deque。deque还有
push_front(将元素插入到前面) 和pop_front(删除最前面的元素)
操作,复杂度是O(1) -
list容器
• 在任何位置插入删除都是常数时间,不支持随机存取。 除了具有所有顺序容器都有的成员函数以外,还支持8个成员函数:
push_front
: 在前面插入
pop_front
: 删除前面的元素
sort
: 排序 ( list 不支持 STL 的算法 sort)
remove
: 删除和指定值相等的所有元素
unique
: 删除所有和前一个元素相同的元素(要做到元素不重复,则unique之前还需要 sort)
merge
: 合并两个链表,并清空被合并的那个
reverse
: 颠倒链表
splice
: 在指定位置前面插入另一链表中的一个或多个元素,并在另一链表中删除被插入的元素 -
函数对象
• 是个对象,但是用起来看上去象函数调用,实际上也执行了函数调用。
• STL里有以下模板:
template<class InIt, class T, class Pred>
T accumulate(InIt first, InIt last, T val, Pred pr);
• pr 就是个函数对象。
对[first,last)中的每个迭代器 I,执行 val = pr(val,* I) ,返回最终的 val。
• pr也可以是个函数。STL 的<functional> 里还有以下函数对象类模板: equal_to greater less ……. 这些模板可以用来生成函数对象。
-
第八周作业
/* // GoodCopy 编写GoodCopy类模板,使得程序按指定方式输出 输入: 第一行是整数 t,表示数据组数 每组数据: 第一行是整数 n , n < 50 第二行是 n 个整数 第三行是 n 个字符串 输出; 将输入的整数原序输出两次,用","分隔 然后将输入的字符串原序输出两次,也用 ","分隔 样例输入 2 4 1 2 3 4 Tom Jack Marry Peking 1 0 Ted 样例输出 1,2,3,4, 1,2,3,4, Tom,Jack,Marry,Peking, Tom,Jack,Marry,Peking, 0, 0, Ted, Ted, */ #include <iostream> using namespace std; template <class T> struct GoodCopy { //your code starts here void operator()(T* s,T* e,T* x) { bool backward = false; T * tmp; for(tmp = s; tmp!= e; ++tmp) { if( tmp == x ) { backward = true; break; } } if( !backward) { for(; s != e; ++s,++x) * x = * s; } else { T * p = x; for(tmp = s; tmp != e; ++tmp,++p); --p; T * q = e; -- q; for(; q != s; --q, --p) * p = * q; *p = * q; } } //your code ends here }; int a[200]; int b[200]; string c[200]; string d[200]; template <class T> void Print(T s,T e) { for(; s != e; ++s) cout << * s << ","; cout << endl; } int main() { int t; cin >> t; while( t -- ) { int m ; cin >> m; for(int i = 0;i < m; ++i) cin >> a[i]; GoodCopy<int>()(a, a+m, b); Print(b, b+m); GoodCopy<int>()(a, a+m, a+m/2); Print(a+m/2, a+m/2 + m); for(int i = 0;i < m; ++i) cin >> c[i]; GoodCopy<string>()(c, c+m, d); Print(c, c+m); GoodCopy<string>()(c, c+m, c+m/2); Print(c+m/2, c+m/2 + m); } system("pause"); return 0; }
/* // 按距离排序 程序填空,输出指定结果 输入 多组数据,每组一行,是一个整数n和一个字符串s 输出 定义两个整数的距离为两个整数差的绝对值 定义两个字符串的距离为两个字符串长度差的绝对值 对每组数据: 对数组a按和n的距离从小到大排序后输出。距离相同的,值小的排在前面。 然后对数组b,按照和s的距离从小到大输出。距离相同的,字典序小的排在前面 样例输入: 2 a123456 4 a12345 样例输出: 1,3,0,4,7,8,9,10,15,20, American,Peking,123456789,Jack,To,abcdefghijklmnop, 4,3,1,7,0,8,9,10,15,20, Peking,American,Jack,123456789,To,abcdefghijklmnop, */ #include <iostream> #include <cmath> #include <algorithm> #include <string> using namespace std; template <class T1,class T2> struct Closer { //your code starts here T1 v; T2 dis; // 构造函数 Closer(T1 n,T2 d):v(n),dis(d) { } // 重载 (), 实现一个函数对象的操作符 bool operator() (const T1 & v1, const T1 & v2) { int d1 = dis(v1, v); int d2 = dis(v2, v); if( d1 < d2) return true; else if( d1 > d2 ) return false; else return v1 < v2; } //your code ends here }; int Distance1(int n1,int n2) { return abs(n1-n2); } int Distance2(const string & s1, const string & s2) { return abs((int)s1.length()- (int) s2.length()); } int a[10] = { 0,3,1,4,7,9,20,8,10,15}; string b[6] = {"American","Jack","To","Peking","abcdefghijklmnop","123456789"}; int main() { int n;string s; while( cin >> n >> s ) { sort(a,a+10, Closer<int ,int (*)(int ,int)> (n, Distance1)); for(int i = 0;i < 10; ++i) cout << a[i] << "," ; cout << endl; sort(b, b+6, Closer<string, int (*)(const string &,const string & )> (s, Distance2)); for(int i = 0; i < 6; ++i) cout << b[i] << "," ; cout << endl; } system("pause"); return 0; }
/* // 很难蒙混过关的CArray3D三维数组模板类 4) 实现一个三维数组模版CArray3D,可以用来生成元素为任意类型变量的三维数组,使得下面程序输出结果是: layer 0: 0,1,2,3,4, 5,6,7,8,9, 10,11,12,13,14, 15,16,17,18,19, layer 1: 20,21,22,23,24, 25,26,27,28,29, 30,31,32,33,34, 35,36,37,38,39, layer 2: 40,41,42,43,44, 45,46,47,48,49, 50,51,52,53,54, 55,56,57,58,59, layer 0: 0,1,2,3,4, 5,6,7,8,9, 10,11,12,13,14, 15,16,17,18,19, layer 1: -1,-1,-1,-1,-1, -1,-1,-1,-1,-1, -1,-1,-1,-1,-1, -1,-1,-1,-1,-1, layer 2: 40,41,42,43,44, 45,46,47,48,49, 50,51,52,53,54, 55,56,57,58,59, layer 0: 0,1,2,3,4, 5,6,7,8,9, 10,11,12,13,14, 15,16,17,18,19, layer 1: -1,-1,-1,-1,-1, 0,0,0,0,0, -1,-1,-1,-1,-1, -1,-1,-1,-1,-1, layer 2: 40,41,42,43,44, 45,46,47,48,49, 50,51,52,53,54, 55,56,57,58,59, layer 0: 10,5, 5,3.33333, layer 1: 5,3.33333, 3.33333,2.5, layer 2: 3.33333,2.5, 2.5,2, **** 7,3.33333 输入: 提示:类里面可以定义类,类模版里面也可以定理类模版。例如: class A { class B { }; }; template <class T> class S { T x; class K { T a; }; }; */ #include <iostream> #include <iomanip> #include <cstring> using namespace std; template <class T> class CArray3D { //your code starts here public: T * p; int L,r,c; //数组共L层,每层r行,每行c列 class Aid { public: T * lp; // lp是某一层的元素的起始地址 int c; //数组共L层,每层r行,每行c列 Aid( T * p_,int c_):lp(p_),c(c_) { } T * operator[]( int r ) { return lp + r * c; //返回值就是本层的第 r行的元素的起始地址 } operator T * () { return lp ; //返回值就是本层的元素的起始地址 } }; CArray3D(int l_,int r_,int c_ ):L(l_),r(r_),c(c_) { p = new T [L * r * c]; } Aid operator[](int L) { //返回的对象里的p记录了第L层元素的起始地址 T * lp = p + L * r * c; return Aid(lp,c); } ~CArray3D() { delete [] p; } //your code ends here }; CArray3D<int> a(3,4,5); CArray3D<double> b(3,2,2); void PrintA() { for(int i = 0;i < 3; ++i) { cout << "layer " << i << ":" << endl; for(int j = 0; j < 4; ++j) { for(int k = 0; k < 5; ++k) cout << a[i][j][k] << "," ; cout << endl; } } } void PrintB() { for(int i = 0;i < 3; ++i) { cout << "layer " << i << ":" << endl; for(int j = 0; j < 2; ++j) { for(int k = 0; k < 2; ++k) cout << b[i][j][k] << "," ; cout << endl; } } } int main() { int No = 0; for( int i = 0; i < 3; ++ i ) { a[i]; for( int j = 0; j < 4; ++j ) { a[j][i]; for( int k = 0; k < 5; ++k ) a[i][j][k] = No ++; a[j][i][i]; } } PrintA(); memset(a[1],-1 ,20*sizeof(int)); memset(a[1],-1 ,20*sizeof(int)); PrintA(); memset(a[1][1],0 ,5*sizeof(int)); PrintA(); for( int i = 0; i < 3; ++ i ) for( int j = 0; j < 2; ++j ) for( int k = 0; k < 2; ++k ) b[i][j][k] = 10.0/(i+j+k+1); PrintB(); int n = a[0][1][2]; double f = b[0][1][1]; cout << "****" << endl; cout << n << "," << f << endl; system("pause"); return 0; }
/* // 函数对象的过滤器 程序填空输出指定结果 输入 多组数据 每组数据两行 第一行是两个整数 m 和 n 第二行先是一个整数k ,然后后面跟着k个整数 输出 对每组数据,按原顺序输出第二行的后k个整数中,大于m且小于n的数输出两遍数据保证一定能找到符合要求的整数 输入样例 1 3 1 2 8 5 1 2 3 4 9 输出样例 2, 2, 3,4, 3,4, */ #include <iostream> #include <vector> using namespace std; struct A { int v; A() { } A(int n):v(n) { }; bool operator<(const A & a) const { return v < a.v; } }; //your code starts here template <class T> class FilterClass { private: T minV,maxV; public: FilterClass(T mi,T ma):minV(mi),maxV(ma) { }; bool operator()(const T & v) const { return minV < v && v < maxV; } }; //your code ends here template <class T> void Print(T s,T e) { for(;s!=e; ++s) cout << *s << ","; cout << endl; } template <class T1, class T2,class T3> T2 Filter( T1 s,T1 e, T2 s2, T3 op) { for(;s != e; ++s) { if( op(*s)) { * s2 = * s; ++s2; } } return s2; } ostream & operator <<(ostream & o,A & a) { o << a.v; return o; } vector<int> ia; vector<A> aa; int main() { int m,n; while(cin >> m >> n) { ia.clear(); aa.clear(); int k,tmp; cin >> k; for(int i = 0;i < k; ++i) { cin >> tmp; ia.push_back(tmp); aa.push_back(tmp); } vector<int> ib(k); vector<A> ab(k); vector<int>::iterator p = Filter(ia.begin(),ia.end(),ib.begin(), FilterClass<int>(m,n)); Print(ib.begin(),p); vector<A>::iterator pp = Filter(aa.begin(),aa.end(),ab.begin(), FilterClass<A>(m,n)); Print(ab.begin(),pp); } system("pause"); return 0; }
/* // 白给的list排序 程序填空,产生指定输出 输入 无 输出 9.8,7.3,3.4,2.6,1.2, 样例输入 无 样例输出 同输入 */ #include <cstdio> #include <iostream> #include <algorithm> #include <list> using namespace std; int main() { double a[] = {1.2,3.4,9.8,7.3,2.6}; list<double> lst(a,a+5); //your code starts here lst.sort(greater<double>()); //your code ends here for(list<double>::iterator i = lst.begin(); i != lst.end(); ++i) cout << * i << "," ; system("pasue"); return 0; }
/* // 我自己的myostream_iteraotr 程序填空输出指定结果 输入: 无 输出: 5,21,14,2,3, 1.4--5.56--3.2--98.3--3.3-- 输入样例 无 输出样例 同输出 */ #include <iostream> #include <list> #include <string> using namespace std; template <class T1,class T2> void Copy(T1 s,T1 e, T2 x) { for(; s != e; ++s,++x) *x = *s; } template<class T> class myostream_iteraotr { //your code starts here ostream & o; string sep; public: myostream_iteraotr( ostream & oo, const string & ss): o(oo),sep(ss) { } myostream_iteraotr & operator ++() { } myostream_iteraotr & operator *() { return * this;} myostream_iteraotr & operator =(const T & t) { o << t << sep; return * this; } //your code ends here }; int main() { const int SIZE = 5; int a[SIZE] = {5,21,14,2,3}; double b[SIZE] = { 1.4, 5.56,3.2,98.3,3.3}; list<int> lst(a,a+SIZE); myostream_iteraotr<int> output(cout,","); Copy( lst.begin(),lst.end(),output); cout << endl; myostream_iteraotr<double> output2(cout,"--"); Copy(b,b+SIZE,output2); system("pause"); return 0; }
// list //By Guo Wei,关键是先sort后unique. #include <list> #include <string> #include <iostream> #include <stdio.h> using namespace std; int main() { list<int> lists[20000]; int n; scanf("%d",&n); char cmd[20]; int id1,id2,num; list<int>::iterator li; while( n -- ) { scanf("%s",cmd); switch(cmd[0]) { case 'a': scanf("%d%d",&id1,&num); lists[id1].push_back(num); break; case 'n': scanf("%d",&id1); break; case 'm': scanf("%d%d",&id1,&id2); lists[id1].merge (lists[id2]); break; case 'u': scanf("%d",&id1); lists[id1].sort(); lists[id1].unique (); break; case 'o': scanf("%d",&id1); lists[id1].sort(); for( li = lists[id1].begin(); li != lists[id1].end(); li ++) printf("%d ",*li); printf("\n"); break; } } system("pause"); return 0; }