STL中的基本概念
- STL(Standard Template Library),标准模板库。
- 容器:容纳各种数据类型的通用数据结构,是类模板
- 迭代器:依次存取容器中的元素,类似指针
- 算法:操作容器中的元素的函数模板
容器的分类
- 顺序容器:vector , deque , list
- 关联容器:set , mutiset , map , mutimap
- 容器适配器:stack , queue , priority_queue
放入容器中的对象,所属的类,应该重载 == 和 “<”运算符
顺序容器
- vector 头文件
动态数组,内存连续,存取元素在常数时间内完成,在尾部删除元素具有较好的性能。
- deque 头文件
双向队列,内存连续,存取元素在常数时间内完成(仅次于vector),在两端删除元素具有较好的性能。
- lIst 头文件
双向链表。元素在内存中不连续存放,在任何位置存取元素都能在常数时间内完成,不支持随机存取。
关联容器
元素是排序的
插入任何元素,都按照其特定的排序规则确定其位置
在查找时具有较好的性能
通常以平衡二叉树的方式实现,插入和检索的时间都是O(log(N))
- set/mutiset 头文件
set就是集合,其中不允许相同元素,mutiset中允许有相同的元素 - map/mutimap 头文件
容器适配器简介
- stack
删除、检索、修改的项只能是最近插入序列的项,即栈顶的项,后进先出
- queue
队列。插入只可以在尾部进行,删除、检索和修改只允许头部进行。先进先出。
- priority_queue
优先级队列,最高优先级的元素总是第一个出队
顺序容器和关联容器中都有的成员函数
- begin:指向容器中第一个元素的迭代器
- end :指向容器中最后一个元素后面位置的迭代器
- rbegin : 指向容器中最后一个元素的迭代器
- rend :指向容器中第一个元素前面位置的迭代器
- erase: 从容器中删除一个或者几个元素
- clear: 从容器中删除所有的元素
顺序容器中的常用成员函数
- front :返回容器中第一个元素的引用
- back :返回容器中最后一个元素的引用
- push_back :在元素尾部添加新元素
- pop_back : 删除元素末尾的元素
- erase :删除迭代器指向的元素,或者删除一个区间,返回被删除元素后面位置的迭代器
迭代器
- 用于指向顺序容器和关联容器中的元素
- 迭代器用法和指针类似
- 有const和非const 两种
- 通过迭代器可以读取它指向的元素
- 通过非const迭代器可以修改它指向的元素
- 定义迭代器的方法:
容器类名::iterator 变量名
或者
容器类名::const_iterator 变量名
访问一个迭代器指向的元素
*迭代器变量名
迭代器可以执行++操作,以使其指向容器中的下一个元素,如果迭代器到达了容器中最后一个元素的后面,此时再使用它,就会报错,类似于使用NULL或者未初始化的指针一样。
#include <cstdlib>
#include <iostream>
#include <vector>
using namespace std;
int main(int argc, char *argv[])
{
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 = 200 ;
}
cout << endl;
for( i = v.begin() ; i != v.end() ; i ++ )
{
cout << *i << " , " ;
}
cout << endl;
system("PAUSE");
return EXIT_SUCCESS;
}
输出结果如下:
双向迭代器
如果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
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 << " " ;
}
ii = v.begin();
while( ii < v.end() )
{
cout << *ii ;
ii = ii + 2 ;
}
list<int> listv;
list<int>::const_iterator vv ;
for( vv = listv.begin() ; vv != listv.end() ; vv++ )
{
cout << *vv << " " ;
}
//错误的用法如下
/*
for( vv = listv.begin() ; vv < list.end() ; vv++ );
for( int i = 0 ; i < listv.size() ; i++ );
*/
算法
- 算法就是一个函数模板,大多数在中定义,
- STL提供能在各种容器中通用的算法,比如查找,排序等
- 算法通过迭代器操作元素。通常可以对容器中的某个局部区间进行操作,因此需要两个参数,一个是起始元素的迭代器,另一个是终止元素后面位置的一个迭代器。比如排序和查找
- 有的算法返回一个迭代器
- 算法可以处理容器,也可以处理普通数组
举例:
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;
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;
}
p = find( v.begin() , v.end() , 9 );
if( p != v.end() )
{
cout << *p <<endl;
}
else
{
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;
}
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) ) << endl;
return 0;
}
用 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;
}
}
程序执行结果:
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 );
cout << v.end() - v.begin() << endl;
//两个随机迭代器可以相减,结果为5
printVector( v.begin() , v.end() ) ;
v.insert( v.begin() + 2 , 13 );
printVector( v.begin() , v.end() );
v.erase( v.begin() + 2 );
printVector( v.begin() , v.end() );
vector<int> v2( 4 , 100 );
//v2中有4个元素,都是100
v2.insert( v2.begin() , v.begin() + 1 , v.begin() + 3 );
// 2 3 100 100 100 100
printVector( v2.begin() , v2.end() );
v.erase( v.begin() + 1 , v.begin() + 3 );//删除 2 3
printVector( v.begin() , v.end() ); // 1 4 5
return 0 ;
}
结果如下:
deque
- 所有适合 vector 的操作都适合于 deque
- deque 还有push_front(将元素插入到前面)和pop_front(删除最前面位置的元素的操作),复杂的为O(1)。
list 容器
- 在任何位置插入删除都是常数时间,不支持随机存取
- 除了具有顺序容器都有的成员函数外,还支持8个成员函数
- push_front :在前面插入
- pop_front :删除前面的元素
- sort:排序,list不支持STL中的算法 sort
- remove:删除和指定值相等的所有元素
- 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 <T>
void printList( const list<T> & lst )
{
//推荐使用两个迭代器作为参数比较好
list<T>::const_iterator i ;
i = lst.begin();
for( ; i != lst.end() ; i++ )
{
cout << *i << " " ;
}
cout << endl;
}
int main()
{
list<A> list1 ,
list2 ;
list1.push_back( 1 );
list1.push_back( 3 );
list1.push_back( 2 );
list1.push_back( 4 );
list1.push_back( 2 );
list2.push_back( 10 );
list2.push_back( 20 );
list2.push_back( 30 );
list2.push_back( 30 );
list2.push_back( 30 );
list2.push_back( 40 );
list2.push_back( 40 );
cout << "list1: " ;
printList( list1 ); // 1 3 2 4 2
cout << "list2: " ;
printList( list2 ); // 10 20 30 30 30 40 40
list2.sort();
cout << "sorted list2: " ;
printList( list2 ); // 10 20 30 30 30 40 40
list2.pop_front();
cout << "list2 pop_front: " ;
printList( list2 ); // 20 30 30 30 40 40
list1.remove( 2 ); // 删除所有和A(2)相等的元素
cout << "list1 remove( 2 ) " ;
printList( list1 ); // 1 3 4
list2.unique(); //删除所有和前一个元素相等的元素
cout << "list2 unique(): " ;
printList( list2 ); // 20 30 40
list1.merge( list2 ); // 合并list2到list1并清空list2
cout << "list1 merge( list2): ";
printList( list1 ); // 1 3 4 20 30 40
list1.reverse();
cout << "list1.reverse(): " ;
printList( list1 );
list2.push_back( 100 );
list2.push_back( 200 );
list2.push_back( 300 );
list2.push_back( 400 );
list<A>::iterator p1 , p2 , p3 ;
p1 = find( list1.begin() , list1.end() , 3 );
p2 = find( list2.begin() , list2.end() , 200 );
p3 = find( list2.begin() , list2.end() , 400 );
list1.splice( p1 , list2 , p2 , p3 );
//将[ p2 , p3 )插入P1之前,并从list2中删除[ p2 , p3 )
cout << "list1 splice( p1 , list2 , p2 , p3 ): " ;
printList( list1 );
// 40 30 20 4 200 300 3 1
cout << "list2 spliced: " ;
printList( list2 );
// 100 400
return 0;
}
结果如下: