STL初始化、读数据、写数据

容器对于编程来说是非常方便的,但是使用不好会出现奇怪的错误,而且很隐蔽。对于其优缺点在此就不多说。此处只讲解下如何使用。(包括如何初始化,写入数据、读出数据这些基本操作)

一、STL简介

STL(Standard TemplateLibrary,标准模板库)是惠普实验室开发的一系列软件的统称。它是由Alexander Stepanov、Meng Lee和David R Musser在惠普实验室工作时所开发出来的。现在虽说它主要出现在C++中,但在被引入C++之前该技术就已经存在了很长的一段时间。

STL的代码从广义上讲分为三类:algorithm(算法)、container(容器)和iterator(迭代器),几乎所有的代码都采用了模板类和模版函数的方式,这相比于传统的由函数和类组成的库来说提供了更好的代码重用机会。在C++标准中,STL被组织为下面的13个头文件:<algorithm>、<deque>、<functional>、<iterator>、<vector>、<list>、<map>、<memory>、<numeric>、<queue>、<set>、<stack>和<utility>

二、STL容器

1)序列式容器(Sequence containers),每个元素都有固定位置--取决于插入时机和地点,和元素值无关,vector、deque、list;

   Vectors:将元素置于一个动态数组中加以管理,可以随机存取元素(用索引直接存取),数组尾部添加或移除元素非常快速。但是在中部或头部安插元素比较费时;

   Deques:是“double-ended queue”的缩写,可以随机存取元素(用索引直接存取),数组头部和尾部添加或移除元素都非常快速。但是在中部安插元素比较费时;

   Lists:双向链表,不提供随机存取(按顺序走到需存取的元素,O(n)),在任何位置上执行插入或删除动作都非常迅速,内部只需调整一下指针;

2)关联式容器(Associated containers),元素位置取决于特定的排序准则,和插入顺序无关,set、multiset、map、multimap;

   Sets/Multisets:内部的元素依据其值自动排序,Set内的相同数值的元素只能出现一次,Multisets内可包含多个数值相同的元素,内部由二叉树实现(实际上基于红黑树(RB-tree)实现),便于查找;

   Maps/Multimaps:Map的元素是成对的键值/实值,内部的元素依据其值自动排序,Map内的相同数值的元素只能出现一次,Multimaps内可包含多个数值相同的元素,内部由二叉树实现(实际上基于红黑树(RB-tree)实现),便于查找;

另外有其他容器hash_map,hash_set,hash_multiset,hash_multimap。

三、STL迭代器 

Iterator(迭代器)模式又称Cursor(游标)模式,用于提供一种方法顺序访问一个聚合对象中各个元素, 而又不需暴露该对象的内部表示。

迭代器的作用:能够让迭代器与算法不干扰的相互发展,最后又能无间隙的粘合起来,重载了*,++,==,!=,=运算符。用以操作复杂的数据结构,容器提供迭代器,算法使用迭代器;

常见的一些迭代器类型:iteratorconst_iteratorreverse_iteratorconst_reverse_iterator


vector和deque容器的迭代器提供额外的运算。list的迭代器不支持算术运算,也不支持关系运算。它只支持前置和后置的自增、自减运算以及相等(不相等)运算。


关系操作符只适用于vector和deque容器,这是因为只有这两种容器为其元素提供快速、随机的访问。他们确保可根据元素位置直接有效地访问指定的容器元素。

四、STL算法

STL算法部分主要由头文件<algorithm>,<numeric>,<functional>组成。要使用 STL中的算法函数必须包含头文件<algorithm>,对于数值算法须包含<numeric><functional>中则定义了一些模板类,用来声明函数对象。
STL
中算法大致分为四类:
1
)、非可变序列算法:指不直接修改其所操作的容器内容的算法。
2
)、可变序列算法:指可以修改它们所操作的容器内容的算法。
3
)、排序算法:包括对序列进行排序和合并的算法、搜索算法以及有序序列上的集合操作。
4
)、数值算法:对容器内容进行数值计算。

Find() 算法在一个区间内寻找合适的元素。如果找到了合适的元素,就返回指向第一个匹配元素的迭代器。否则,返回的值指向区间的尾部。要得到被找到元素的下标,必须用 find() 返回的结果减去初始迭代器。

五、适配器

STL提供了三个容器适配器:queue、priority_queue、stack。这些适配器都是包装了vectorlistdeque中某个顺序容器的包装器。注意:适配器没有提供迭代器,也不能同时插入或删除多个元素。

六、各容器介绍

6.1 vector

vector<int> v(20,1);这样才代表申请vector大小为20,并全部初始化为1. 




小结:vectorreserve增加了vectorcapacity,但是它的size没有改变!而resize改变了vectorcapacity同时也增加了它的size!这是因为:(1reserve是为容器预留空间,但在空间内不真正创建元素对象,所以在没有添加新的对象之前,不能引用容器内的元素。加入新的元素时,要调用push_back()/insert()函数。(2resize是改变容器的大小,且在创建对象,因此,调用这个函数之后,就可以引用容器内的对象了,因此当加入新的元素时,用operator[]操作符,或者用迭代器来引用元素对象。此时再调用push_back()函数,是加在这个新的空间后面的。


vector和数组的主要区别是什么呢?这对于理解vector是很有帮助的。

数组:分配的是静态空间,一般分配了就不可以改变,就像我们熟知的定义了一个数组,那么数组的长度就不可以改变了,我们也不可以进行越界访问,但是编译器不检查越界,这一点在我们编程的时候要尤为注意(很多都可能会烦这样的错误!!)。一般申请的数组长度不能满足我们的要求了,我们要重新申请大一点数组,然后把原数组中数据复制过来。

vector分配的是动态空间,即:我们发现在声明vector容器的时候也可以不指定容器的大小,vector是随着元素的加入,空间自动扩展的。但是,我们必须要负责任的肯定vector分配的空间是连续的,也就是支持数组中的下标随机访问,实际上vector的实现机制是:预留一部分空间,而且预留空间的大小是按一定比率增长的,如果空间不够用的话,要保证连续,就必须重新new一片空间,然后将原有元素移动到新空间,同时预留新的空间(并且新分配的空间比原来分配的空间),最后将原来的那部分空间释放掉。这样预留空间的好处就是不用每次向vector中加元素都重新分配空间。

小结:注意这种:vector <Elem> c(beg,end)声明方式,创建一个和[beg;end)区间元素相同的vector,一定要注意是左闭右开区间,同时需要说的是,STL中不论是容器还是算法都是采用的这种左闭右开区间办事的,包括v.end()函数也是返回的vector末端的下位置,相当于int a[n]的a[n],并不能访问~~~

6.2 set

关于set,必须说明的是set关联式容器。set作为一个容器也是用来存储同一数据类型,在set中每个元素的值都唯一,而且系统能根据元素的值自动进行排序。应该注意的是set中元素的值不能直接被改变。C++ STL中标准关联容器set,multiset, map, multimap内部采用的就是一种非常高效的平衡检索二叉树:红黑树,也成为RB(Red-Black Tree)RB树的统计性能要好于一般平衡二叉树,所以被STL选择作为了关联容器的内部结构。

std::set<int> s;s这个对象里面存贮的元素是从小到大排序的,(因为用std::less作为比较工具。不过可以修改)

关于set有下面几个问题:

1)为何mapset的插入删除效率比用其他序列容器高?

应该改成为何mapset的插入删除效率比用vector容器高?因为与list相比,mapset明显效率要低(O(1) vsO(lgn)

大部分人说,很简单,因为对于关联容器来说,不需要做内存拷贝和内存移动。set容器内所有元素都是以节点的方式来存储,其节点结构和链表差不多,指向父节点和子节点。

2)为何每次insert之后,以前保存的iterator不会失效?

iterator这里就相当于指向节点的指针,内存没有变,指向内存的指针怎么会失效呢(当然被删除的那个元素本身已经失效了)。相对于vector来说,每一次删除和插入,指针都有可能失效,调用push_back在尾部插入也是如此。因为为了保证内部数据的连续存放,iterator指向的那块内存在删除和插入过程中可能已经被其他内存覆盖或者内存已经被释放了。即使时push_back的时候,容器内部空间可能不够,需要一块新的更大的内存,只有把以前的内存释放,申请新的更大的内存,复制已有的数据元素到新的内存,最后把需要插入的元素放到最后,那么以前的内存指针自然就不可用了。特别时在和find等算法在一起使用的时候,牢记这个原则:不要使用过期的iterator

3)当数据元素增多时,set的插入和搜索速度变化如何?

set中查找是使用二分查找,也就是说,如果有16个元素,最多需要比较4次就能找到结果,有32个元素,最多比较5次。那么有10000个呢?最多比较的次数为log10000,最多为14次,如果是20000个元素呢?最多不过15次。看见了吧,当数据量增大一倍的时候,搜索次数只不过多了1次,多了1/14的搜索时间而已。

set中常用的方法


begin() ,返回set容器的第一个元素

end(),返回set容器的最后一个元素的后一个()

clear() ,删除set容器中的所有的元素

empty(),判断set容器是否为空

max_size(),返回set容器可能包含的元素最大个数

size(),返回当前set容器中的元素个数

rbegin ,返回的值和end()相同

rend() ,返回的值和rbegin()相同

注意begin() end()函数是不检查set是否为空的,使用前最好使用empty()检验一下set是否为空.

erase(iterator)  ,删除定位器iterator指向的值

erase(first,second),删除定位器firstsecond之间的值

erase(key_value),删除键值key_value的值

小结:set中的删除操作是不进行任何的错误检查的,比如定位器的是否合法等等,所以用的时候自己一定要注意。

insert(key_value); key_value插入到set ,返回值是pair<set<int>::iterator,bool>bool标志着插入是否成功,而iterator代表插入的位置,若key_value已经在set中,则iterator表示的key_valueset中的位置。

inset(first,second);将定位器firstsecond之间的元素插入到set中,返回值是void.

lower_bound(key_value) ,返回第一个大于等于key_value的定位器

upper_bound(key_value)返回最后一个大于等于key_value的定位器


使用‘find()’成员函数确定集合 set 中是否存在某个元素。不要搞混了,因为 STL 中有很多‘find()’。有一个全局算法‘find()’,输入两个迭代器和一个元素,它能工作在 O(N) 的线性时间复杂度下。你可能会用它来搜索 set 中的元素,但是明明存在一个 O(log N) 时间复杂度的算法,为何要用一个 O(N) 的算法呢?在 set 和 map (还包括 multiset/multimap、hash_map/hash_set等容器)中搜索元素时,不要使用全局的搜索函数 find() ——反而应该使用成员函数‘set::find()’。作为‘顺序的’find函数,set::find 会返回一个迭代器,不论这个迭代器指向被找到的元素,还是指向‘end()’。

由于 set 不是线性容器,不可能用下标获取 set 中的元素。因此,遍历 set 元素的唯一方法就是使用迭代器。

set 可以在 O(log N)(其中 N 是 set 中对象的个数)的时间复杂度下添加、移除元素,并检查特定元素是否存在。向 set 添加元素时,如果和已有元素值重复,那新添加的元素就会被抛弃。在常数时间复杂度 O(1) 下返回 set 的元素个数。

set/multiset会根据待定的排序准则,自动将元素排序。两者不同在于前者不允许元素重复,而后者允许。
1) 不能直接改变元素值,因为那样会打乱原本正确的顺序,要改变元素值必须先删除旧元素,则插入新元素
2) 不提供直接存取元素的任何操作函数,只能通过迭代器进行间接存取,而且从迭代器角度来看,元素值是常数
3) 元素比较动作只能用于型别相同的容器(即元素和排序准则必须相同)

6.3 map

1、关于map的介绍

mapSTL的一个容器,和set一样,map也是一种关联式容器。它提供一对一(其中第一个可以称为关键字,每个关键字只能在map中出现一次,第二个可称为该关键字的值)的数据处理能力,由于这个特性,有助于我们处理一对一数据。这里说下map内部数据的组织,map内部是自建一颗红黑树(一种非严格意义上的平衡二叉树),这颗树具有对数据自动排序的功能,所以在map内部所有的数据都是有序的。学习map我们一定要理解什么是一对一的数据映射?比如:一个班级中,每个学生的学号跟他的姓名就存在着一一映射的关系,这个模型用map可能轻易描述,很明显学号用int描述,姓名用字符串描述采用string,于是我们使用的map形式如下:map<int , string> student;

对于map中的每个节点存储的是一对信息,包括一个键和一个值,各个节点之间的键值不能重复。

对于set中的每个节点存储的是一个信息,只有一个键,但是每个键值也是唯一的。set表示的是集合的概念。

对于map的学习,或者说是对STL中的容器的学习,要知道每种容器的实现原理,每种适合解决什么问题的。

map是一类关联式容器。它的特点是增加和删除节点对迭代器的影响很小,除了那个操作节点,对其他的节点都没有什么影响。对于迭代器来说,可以修改实值,而不能修改key
2、map的功能
自动建立Key  value的对应。key  value可以是任意你需要的类型。 
根据key值快速查找记录,查找的复杂度基本是Log(N),如果有1000个记录,最多查找10次,1,000,000个记录,最多查找20次。 
快速插入Key - Value 记录。 
快速删除记录 
根据Key 修改value记录。 
遍历所有记录。

和所有标准的关联式容器一样,map/multimap也是平衡二叉树,setvaluekey指向同一个对象,map元素是key/valuepairmap可以作为关联式数组使用,map也是通过key的值进行排序的,所以搜索元素有很好的性能,其keyconst类型,不可以直接改变,要修改的话必须先删除这个元素,再插入一个新的,但value是可以改变的。 
3、使用map

4、在map中插入元素
改变map中的条目非常简单,因为map类已经对[]操作符进行了重载
enumMap[1] = "One";
enumMap[2] = "Two";
这样非常直观,但存在一个性能的问题。插入2时,先在enumMap中查找主键为2的项,没发现,然后将一个新的对象插入enumMap,键是2,值是一个空字符串,插入完成后,将字符串赋为"Two"; 该方法会将每个值都赋为缺省值,然后再赋为显示的值,如果元素是类对象,则开销比较大。我们可以用以下方法来避免开销:
enumMap.insert(map<int, CString> :: value_type(2, "Two"))
5、查找并获取map中的元素
下标操作符给出了获得一个值的最简单方法:
CString tmp = enumMap[2];
但是,只有当map中有这个键的实例时才对,否则会自动插入一个实例,值为初始化值。
我们可以使用Find()和Count()方法来发现一个键是否存在。
查找map中是否包含某个关键字条目用find()方法,传入的参数是要查找的key,在这里需要提到的是begin()和end()两个成员,分别代表map对象中第一个条目和最后一个条目,这两个数据的类型是iterator.

通过map对象的方法获取的iterator数据类型是一个std::pair对象,包括两个数据 iterator->first 和 iterator->second 分别代表关键字和存储的数据。

实际上,map 非常像 set,除了一点——它包含的不只是值而是键值对 pair<key, value>。Map 保证最多只有一个键值对拥有指定键。另一个很讨喜的地方是, map 定义了下标运算符 []。

不要通过迭代器来更改 map 元素的键,因为这可能破坏 map 内部数据结构的完整性(见下面的解释)。

在 map::find() 和 map::operator [] 之间有一个重要的区别。Map::find() 永远不会改变 map 的内容,而操作符 [] 则会在元素不存在时创建一个新元素。有时这样做很方便,但当你不想添加新元素时,在循环中多次使用操作符 [] 绝对不是好主意。这就是为什么把 map 作为常引用参数传递给某个函数时,可能不用操作符 [] 的原因:

6、关于 Map 和 Set 的注意事项

从内部看,map 和 set 几乎都是以红黑树的结构存储。我们确实不必担忧内部结构,要记住的是,遍历容器时 map 和 set 的元素总是按升序排列。而这也是为何在遍历 map 或 set时,极力不推荐改变键值的原因:如果所做的修改破坏了元素间的顺序,这至少会导致容器的算法失效。另一件重要的事情是,map 和 set 的迭代器都定义了运算符 ++ 和 –-。

大部分算法都声明在标准头文件 #include <algorithm> 中。首先,STL 提供了三种很简单的算法:min(a, b)、max(a, b)、swap(a, b)。这里,min(a, b) 和 max(a, b) 分别返回两个元素间的最小值和最大值,而 swap(a, b) 则交换两个元素的值。

算法 sort() 的使用也很普遍。调用 sort(begin,end) 按升序对一个区间的元素进行排序。注意,sort() 需要随机存取迭代器,因此它不能作用在所有类型的容器上。无论如何,你很可能永远都不会对已然有序的 set 调用 sort()。

你已经了解了算法 find()。调用 find(begin,end, element) 返回‘element’首次出现时对应的迭代器,如果找不到则返回 end。和 find(…) 相反,count(begin, end, element) 返回一个元素在容器或容器的某个范围内出现的次数。记住,set 和 map 都有成员函数find() 和 count(),它们的时间复杂度是O(log N),而 std::find() 和std::count() 的时间复杂度是 O(N)。

 

map是STL的一个关联容器,它提供一对一(其中第一个可以称为关键字,每个关键字只能在map中出现一次,第二个可能称为该关键字的值)的数据处理能力,由于这个特性,它完成有可能在我们处理一对一数据的时候,在编程上提供快速通道。这里说下map内部数据的组织,map内部自建一颗红黑树(一种非严格意义上的平衡二叉树),这颗树具有对数据自动排序的功能,所以在map内部所有的数据都是有序的。

6.4 关于list容器

list是一种序列式容器。list容器完成的功能实际上和数据结构中的双向链表是极其相似的,list中的数据元素是通过链表指针串连成逻辑意义上的线性表,也就是list也具有链表的主要优点,即:在链表的任一位置进行元素的插入、删除操作都是快速的。list的实现大概是这样的:list的每个节点有三个域:前驱元素指针域、数据域和后继元素指针域。前驱元素指针域保存了前驱元素的首地址;数据域则是本节点的数据;后继元素指针域则保存了后继元素的首地址。其实,list和循环链表也有相似的地方,即:头节点的前驱元素指针域保存的是链表中尾元素的首地址,list的尾节点的后继元素指针域则保存了头节点的首地址,这样,list实际上就构成了一个双向循环链。由于list元素节点并不要求在一段连续的内存中,显然在list中是不支持快速随机存取的,因此对于迭代器,只能通过“++”“--”操作将迭代器移动到后继/前驱节点元素处。而不能对迭代器进行+n-n的操作,这点,是与vector等不同的地方。

vectorvectorbuilt-in数组类似,拥有一段连续的内存空间,能非常好的支持随即存取,即[]操作符,但由于它的内存空间是连续的,所以在中间进行插入和删除会造成内存块的拷贝,另外,当插入较多的元素后,预留内存空间可能不够,需要重新申请一块足够大的内存并把原来的数据拷贝到新的内存空间。这些影响了vector的效率,但是实际上用的最多的还是vector容器,建议大多数时候使用vector效率一般是不错的。

listlist就是数据结构中的双向链表(根据sgistl源代码),因此它的内存空间是不连续的,通过指针来进行数据的访问,这个特点使得它的随即存取变的非常没有效率,因此它没有提供[]操作符的重载。但由于链表的特点,它可以以很好的效率支持任意地方的删除和插入。

dequedeque是一个double-ended queue,它的具体实现不太清楚,但知道它具有以下两个特点:它支持[]操作符,也就是支持随即存取,并且和vector的效率相差无几,它支持在两端的操作:push_back,push_front,pop_back,pop_front等,并且在两端操作上与list的效率也差不多。

因此在实际使用时,如何选择这三个容器中哪一个,应根据你的需要而定,具体可以遵循下面的原则
1. 如果你需要高效的随机存取,而不在乎插入和删除的效率,使用vector
2. 如果你需要大量的插入和删除,而不关心随机存取,则应使用list
3. 如果你需要随机存取,而且关心两端数据的插入和删除,则应使用deque

6.5 queue

queue 的基本操作有:
入队,如例:q.push(x); 将x 接到队列的末端。
出队,如例:q.pop(); 弹出队列的第一个元素(队首元素,删除了),注意,并不会返回被弹出元素的值。
访问队首元素,如例:q.front(),即最早被压入队列的元素。(只是访问,元素还在。想删除要有pop)
访问队尾元素,如例:q.back(),即最后被压入队列的元素。
判断队列空,如例:q.empty(),当队列空时,返回true。
访问队列中的元素个数,如例:q.size()

deque是双向的,可以删除队尾元素。(queue不可以删除队尾元素)


6.6 Deque容器

deque容器是C++标准模版库(STL,Standard Template Library)中的部分内容。deque双向队列是一种双向开口的连续线性空间,可以高效的在头尾两端插入和删除元素,deque容器类与vector类似,支持随机访问和快速插入删除,它在容器中某一位置上的操作所花费的是线性时间。vector不同的是,deque还支持从开始端插入数据push_front()等。

c.front()返回c容器的第一个元素

c.back()返回c容器的最后一个元素

c.operator[]下标运算符重载

operator=赋值运算符重载

c.push_back(num)在末尾位置插入元素

c.pop_back()删除末尾位置的元素

c.push_front(num)在开头位置插入元素

c.pop_front()删除开头位置的元素

c.insert(pos,num)pos位置插入元素num

c.insert(pos,n,num)pos位置插入n个元素num

c.insert(pos,beg,end)pos位置插入区间为[beg,end)的元素


6.7 stack

栈是一种容器适配器,特别为后入先出而设计的一种(LIFO),那种数据被插入,然后再容器末端取出。

栈(statck)这种数据结构在计算机中是相当出名的。栈中的数据是先进后出的(First In Last Out, FILO)。栈只有一个出口,允许新增元素(只能在栈顶上增加)、移出元素(只能移出栈顶元素)、取得栈顶元素等操作。在STL中,栈是以别的容器作为底部结构,再将接口改变,使之符合栈的特性就可以了。因此实现非常的方便。下面就给出栈的函数列表和VS2008中栈的源代码,在STL中栈一共就5个常用操作函数(top()push()pop() size()empty() ),很好记的。


6.8 priority_queue

STL提供给你更加简单的库,就是priority_queue,其时间复杂度也只有o(nlogn)。

priority_queue<Type, Container, Functional>

Type为数据类型, Container为保存数据的容器,Functional为元素比较方式。

如果不写后两个参数,那么容器默认用的是vector,比较方式默认用operator<,也就是优先队列是大顶堆,队头元素最大。

priority_queue提供了三个基本函数,分别是:

1.    top()

2.    push()

3.    pop()

注意,pop并不会返回元素,top才会返回队首的元素。

STL提供了仿函数greater<>,less<>,简化了自己再定义排序函数的过程。如果你想使用自己定义的结构,而不想使用基本数据类型,也是ok的,不过你需要在你自定义的类中重载运算符。

// TEMPLATE STRUCT greater  
emplate<class _Ty>
	struct greater
		: public binary_function<_Ty, _Ty, bool>
	{   // functor for operator>  
		bool operator()(const _Ty& _Left, const _Ty& _Right) const
		{   // apply operator> to operands  
			return (_Left > _Right);
		}
	};
// TEMPLATE STRUCT less  
emplate<class _Ty>
	struct less
		: public binary_function<_Ty, _Ty, bool>
	{   // functor for operator<  
		bool operator()(const _Ty& _Left, const _Ty& _Right) const
		{   // apply operator< to operands  
			return (_Left < _Right);
		}
	};
对照着上面可以自己定义一个比较函数
#include <string>
#include <queue>
#include<functional>//因为使用了greater<int>()
#include <iostream>  
#include<cstdlib>  
using namespace std;

struct Node{
	int x, y;
	Node(int x_value = 0, int y_value = 0) :x(x_value), y(y_value) {}
};
//这个含义是:因为定义node相当于坐标,先以x上的值比较大小,如果相等则以y的值比较。似乎是如果返回true,这两个元素就需要交换位置,反之则不需要。
struct cmp{
	bool operator()(Node a, Node b){
		if (a.x == b.x)  return a.y>b.y;
		return a.x>b.x;
	}
};
int main()
{
	priority_queue<int> p;
	p.push(1);
	p.push(8);
	p.push(5);
	for (int i = 0; i<3; i++)
	{
		cout << p.top() << " ";
		p.pop();
	}
	cout << endl;
	priority_queue<int, vector<int>, greater<int> >p_greater;
	p_greater.push(1);
	p_greater.push(8);
	p_greater.push(5);
	for (int i = 0; i<3; i++)
	{
		cout << p_greater.top() << " ";
		p_greater.pop();
	}
	cout << endl;
	priority_queue<Node, vector<Node>, cmp>p_myself;
	p_myself.push(Node(5, 5));
	p_myself.push(Node(1, 5));
	p_myself.push(Node(5, 1));

	while (!p_myself.empty()){
		cout << p_myself.top().x << ' ' << p_myself.top().y << endl;
		p_myself.pop();
	}
	system("pause");
	return 0;
}

什么时候需要用multiset?当然是需要用set,但是又允许重复key存在的时候了。什么时候用set?我的答案是:需要随时往容器中插入元素,随时对元素进行快速查找,又需要按某种顺序对元素进行遍历的时候——如果没有第三项需求的话可以用非标准库的hash_或标准库的unordered_开头的容器。

举一个wikipedia上的一个例子:假设我们需要将一个很大的数n分解为多个质数因子的乘积,并将这些质数因子存储在容器中,供以后查询和遍历用。比如对于n = 120 = 2 x 2 x 2 x 3 x 5,我们可以将它的质数因子存储为primeFactorMultiset = { 2, 2, 2, 3, 5 }。当然也可以用map,将质数因子本身存为key,将该因子的出现次数存为value。

猜你喜欢

转载自blog.csdn.net/E_ROAD_BY_U/article/details/70244943