容器 |
特性 |
所在头文件 |
向量vector |
可以用常数时间访问和修改任意元素,在序列尾部进行插入和删除时,具有常数时间复杂度,对任意项的插入和删除就有的时间复杂度与到末尾的距离成正比,尤其对向量头的添加和删除的代价是惊人的高的 |
<vector> |
双端队列deque |
基本上与向量相同,唯一的不同是,其在序列头部插入和删除操作也具有常量时间复杂度 |
<deque> |
表list |
对任意元素的访问与对两端的距离成正比,但对某个位置上插入和删除一个项的花费为常数时间。 |
<list> |
队列queue |
插入只可以在尾部进行,删除、检索和修改只允许从头部进行。按照先进先出的原则。 |
<queue> |
堆栈stack |
堆栈是项的有限序列,并满足序列中被删除、检索和修改的项只能是最近插入序列的项。即按照后进先出的原则 |
<stack> |
集合set |
由节点组成的红黑树,每个节点都包含着一个元素,节点之间以某种作用于元素对的谓词排列,没有两个不同的元素能够拥有相同的次序,具有快速查找的功能。但是它是以牺牲插入车删除操作的效率为代价的 |
<set> |
多重集合multiset |
和集合基本相同,但可以支持重复元素具有快速查找能力 |
<set> |
映射map |
由{键,值}对组成的集合,以某种作用于键对上的谓词排列。具有快速查找能力 |
<map> |
多重集合multimap |
比起映射,一个键可以对应多了值。具有快速查找能力 |
<map> |
考虑到不同的实际需要,更主要的是效率的需要,我们可以选择不同的容器来实现我们的程序,以此达到我们提高性能的目的。
二、算法
算法部分主要由头文件<algorithm>,<numeric>和<functional>组成。<algorithm>是所有STL头文件中最大的一个,它是由一大堆模版函数组成的,可以认为每个函数在很大程度上都是独立的,其中常用到的功能范围涉及到比较、交换、查找、遍历操作、复制、修改、移除、反转、排序、合并等等。<numeric>体积很小,只包括几个在序列上面进行简单数学运算的模板函数,包括加法和乘法在序列上的一些操作。<functional>中则定义了一些模板类,用以声明函数对象。
STL的算法也是非常优秀的,它们大部分都是类属的,基本上都用到了C++的模板来实现,这样,很多相似的函数就不用自己写了,只要用函数模板就OK了。
我们使用算法的时候,要针对不同的容器,比如:对集合的查找,最好不要用通用函数find(),它对集合使用的时候,性能非常的差,最好用集合自带的find()函数,它针对了集合进行了优化,性能非常的高。
三、迭代器
迭代器提供对一个容器中的对象的访问方法,并且定义了容器中对象的范围。迭代器就如同一个指针。事实上,C++的指针也是一种迭代器。但是,迭代器不仅仅是指针,因此你不能认为他们一定具有地址值。例如,一个数组索引,也可以认为是一种迭代器。
迭代器有各种不同的创建方法。程序可能把迭代器作为一个变量创建。一个STL容器类可能为了使用一个特定类型的数据而创建一个迭代器。作为指针,必须能够使用*操作符类获取数据。你还可以使用其他数学操作符如++。典型的,++操作符用来递增迭代器,以访问容器中的下一个对象。如果迭代器到达了容器中的最后一个元素的后面,则迭代器变成past-the-end值。使用一个past-the-end值得指针来访问对象是非法的,就好像使用NULL或为初始化的指针一样。
提示
STL不保证可以从另一个迭代器来抵达一个迭代器。例如,当对一个集合中的对象排序时,如果你在不同的结构中指定了两个迭代器,第二个迭代器无法从第一个迭代器抵达,此时程序注定要失败。这是STL灵活性的一个代价。STL不保证检测毫无道理的错误。
它的具体实现在<itertator> 中,我们完全可以不管迭代器类是怎么实现的,大多数的时候,把它理解为指针是没有问题的(指针是迭代器的一个特例,它也属于迭代器),但是,决不能完全这么做。
迭代器功能(Abilities Of Iterator Gategories) | ||
输入迭代器 Input iterator |
向前读 Reads forward |
istream |
输出迭代器 Output iterator |
向前写 Writes forward |
ostream,inserter |
前向迭代器 Forward iterator |
向前读写 Read and Writes forward |
|
双向迭代器 Bidirectional iterator |
向前向后读写 Read and Writes forward and backward |
list,set,multiset,map,mul timap |
随机迭代器 Random access iterator |
随机读写 Read and Write with random access |
vector,deque,array,string |
由此可见,指针和迭代器还是有很大差别的。和指针最接近的就是随机访问迭代器
#include"stdafx.h" #include <iostream.h> #include <algorithm> using namespace std; #define SIZE 100 int iarray[SIZE]; int main() { iarray[20] = 50; int* ip = find(iarray, iarray + SIZE, 50); if (ip != NULL) // 当使用STL函数时,只能测试ip是否和past-the-end 值是否相等。尽管在本例中ip是一个C++指针,其用法也必须符合STL迭代器的规则。 if (ip == iarray + SIZE) // 为了判断find()是否成功,例子中测试ip和 past-the-end 值是否相等: cout << "50 not found in array" << endl; //如果表达式为真,则表示在搜索的范围内没有指定的值。否则就是指向一个合法对象的指针 else cout << *ip << " found in array" << endl; // return 0; }
尽管C++指针也是迭代器,但用的更多的是容器迭代器。容器迭代器用法和iterdemo.cpp一样,但和将迭代器申明为指针变量不同的是,你可以使用容器类方法来获取迭代器对象。两个典型的容器类方法是begin()和end()。它们在大多数容器中表示整个容器范围。其他一些容器还使用rbegin()和rend()方法提供反向迭代器,以按反向顺序指定对象范围。
下面的程序创建了一个矢量容器(STL的和数组等价的对象),并使用迭代器在其中搜索。该程序和前面的程序相同。
#include"stdafx.h" #include <iostream.h> #include <algorithm> #include <vector> using namespace std; vector<int> intVector(100); void main() { intVector[20] = 50; vector<int>::iterator intIter = find(intVector.begin(), intVector.end(), 50); if (intIter != intVector.end()) cout << "Vector contains value " << *intIter << endl; else cout << "Vector does not contain 50" << endl; }
输入迭代器
#include"stdafx.h" #include"stdafx.h" #include <iostream.h> #include <algorithm> #include <vector> using namespace std; vector<int> intVector(100); //输入迭代器是最普通的类型。输入迭代器至少能够使用==和!=测试是否相等;使用*来访问数据;使用++操作来递推迭代器到下一个元素或到达past-the-end 值。 //修改一下测试 呵呵 template <class InputIterator, class T> InputIterator find2(InputIterator first, InputIterator last, const T& value) { while (first != last && *first != value) ++first; return first; } void main() { intVector[20] = 5; vector<int>::iterator intIter; //和指针一样,你可以给一个迭代器赋值。例如,首先申明一个迭代器 intIter=find2(intVector.begin(), intVector.end(), 50); if (intIter != intVector.end()) cout << "Vector contains value " << *intIter << endl; else cout << "Vector does not contain 50" << endl; getchar(); }
#include"stdafx.h" #include <iostream.h> #include <algorithm> // Need copy() #include <vector> // Need vector using namespace std; double darray[10] = {1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9}; vector<double> vdouble(10); int main() { vector<double>::iterator outputIterator = vdouble.begin(); copy(darray, darray + 10, outputIterator); while (outputIterator != vdouble.end()) { cout << *outputIterator << endl; outputIterator++; } return 0; }
template <class ForwardIterator, class T> void replace (ForwardIterator first, ForwardIterator last, const T& old_value, const T& new_value);
replace(vdouble.begin(), vdouble.end(), 1.5, 3.14159);
双向迭代器
/*双向迭代器要求能够增减。如reverse()算法要求两个双向迭代器作为参数: */ template <class BidirectionalIterator> void reverse (BidirectionalIterator first, BidirectionalIterator last); //使用reverse()函数来对容器进行逆向排序: reverse(vdouble.begin(), vdouble.end());
随机访问迭代器
/*随机访问迭代器能够以任意顺序访问数据,并能用于读写数据(不是const的C++指针也是随机访问迭代器)。STL的排序和搜索函数使用随机访问迭代器。随机访问迭代器可以使用关系操作符作比较。 */ //random_shuffle() 函数随机打乱原先的顺序。申明为: template <class RandomAccessIterator> void random_shuffle (RandomAccessIterator first, RandomAccessIterator last); //使用方法: random_shuffle(vdouble.begin(), vdouble.end());对于迭代器,有另一种方法使用流和标准函数。理解的要点是将输入/输出流作为容器看待。因此,任何接受迭代器参数的算法都可以和流一起工作。
#include"stdafx.h" #include <iostream> #include <stdlib.h> // Need random(), srandom() #include <time.h> // Need time() #include <algorithm> // Need sort(), copy() #include <vector> // Need vector #include <iterator> using namespace std; void Display(vector<int>& v, const char* s); int main() { // Seed the random number generator srand ( time(NULL) ); // Construct vector and fill with random integer values vector<int> collection(10); for (int i = 0; i < 10; i++) collection[i] = rand() % 10000;; // Display, sort, and redisplay Display(collection, "Before sorting"); sort(collection.begin(), collection.end()); Display(collection, "After sorting"); return 0; } // Display label s and contents of integer vector v void Display(vector<int>& v, const char* s) { cout << endl << s << endl; copy(v.begin(), v.end(), ostream_iterator<int>(cout, "\t")); cout << endl; }
插入迭代器
插入迭代器用于将值插入到容器中。它们也叫做适配器,因为它们将容器适配或转化为一个迭代器,并用于copy()这样的算法中。例如,一个程序定义了一个链表和一个矢量容器:
list<double> dList;vector<double> dVector;通过使用front_inserter迭代器对象,可以只用单个copy()语句就完成将矢量中的对象插入到链表前端的操作:
copy(dVector.begin(), dVector.end(), front_inserter(dList));三种插入迭代器如下:
· 普通插入器 将对象插入到容器任何对象的前面。
· Front inserters 将对象插入到数据集的前面——例如,链表表头。
· Back inserters 将对象插入到集合的尾部——例如,矢量的尾部,导致矢量容器扩展。
使用插入迭代器可能导致容器中的其他对象移动位置,因而使得现存的迭代器非法。例如,将一个对象插入到矢量容器将导致其他值移动位置以腾出空间。一般来说,插入到象链表这样的结构中更为有效,因为它们不会导致其他对象移动。
#include"stdafx.h" #include <iostream> #include <algorithm> #include <list> #include <iterator> using namespace std; int iArray[5] = { 1, 2, 3, 4, 5 }; void Display(list<int>& v, const char* s); int main() { list<int> iList; // Copy iArray backwards into iList copy(iArray, iArray + 5, front_inserter(iList));Display(iList, "Before find and copy"); // Locate value 3 in iList list<int>::iterator p = find(iList.begin(), iList.end(), 3); // Copy first two iArray values to iList ahead of p copy(iArray, iArray + 2, inserter(iList, p)); Display(iList, "After find and copy"); return 0; } void Display(list<int>& a, const char* s) { cout << s << endl; copy(a.begin(), a.end(), ostream_iterator<int>(cout, " ")); cout << endl; }
//功能是分别对数组,向量,表,多重集合进行插入操作,对每个容器插入100万个随机整数; #include"stdafx.h" #include<iostream> #include<iterator> #include<vector> #include<list> #include<set> #include<time.h> #include<conio.h> #include<algorithm> using namespace std; template<typename T> void arrayInsert(T*a,T*s,long size) //向数组插入数据 { //for(long i=0;i<10;i++) // //好像数组支持不到100万,我们就算10万的 //最后在把把结果乘以10吧, //{ for(long k=0;k<size;k++) { a[k]=s[k]; } //} } template<typename T> void vectorInsert( vector<T> *v,T*s,long size) //向向量插入数据 { for(int i=0;i<10;i++) { for(long k=0;k<size;k++) { v->push_back(s[k]); } } } template<typename T> void listInsert(list<T>*l,T*s,long size) //向表插入数据 { for(int i=0;i<10;i++) { for(long k=0;k<size;k++) { l->push_back(s[k]); } } } template<class T> void multisetInsert(multiset<T>*s1,T*s,long size) //向多重集合插入数据 { for(int i=0;i<10;i++) { for(long k=0;k<size;k++) { s1->insert(s[k]); } } } int* genIntData(long size) //生成随机数 { int* data=new int[size]; generate(&data[0],&data[size],rand); return data; } int main(void) { const long size=100000; int* s_data,array1[size]; double begin,end; s_data=genIntData(size); vector<int> vector1; list<int> list1; multiset<int> multiset1; clock(); cout<<"?"<<endl; begin=(double)clock()/CLK_TCK; arrayInsert<int>(array1,s_data,size); end=(double)clock()/CLK_TCK; cout<<"??"<<(end-begin)<<endl; getch(); begin=(double)clock()/CLK_TCK; vectorInsert<int>(&vector1,s_data,size); end=(double)clock()/CLK_TCK; cout<<"??"<<(end-begin)<<endl; getch(); begin=(double)clock()/CLK_TCK; listInsert<int>(&list1,s_data,size); end=(double)clock()/CLK_TCK; cout<<"??"<<(end-begin)<<endl; getch(); begin=(double)clock()/CLK_TCK; multisetInsert<int>(&multiset1,s_data,size); end=(double)clock()/CLK_TCK; cout<<"??"<<(end-begin)<<endl; getch(); free(s_data); return 0; }