参考上位:
https://blog.csdn.net/u010183728/article/details/81913729#2.4.2%20queue%20%26%20priority_queue
https://www.cnblogs.com/yelongsan/p/4049698.html
顺序容器vector
连续存储结构,每个元素在内存上是连续的;支持 高效的随机访问和在尾端插入/删除操作,但其他位置的插入/删除操作效率低下;
#include < vector>
using namespace std;
构造
vector():创建一个空vector
vector(int nSize):创建一个vector,元素个数为nSize
vector(int nSize,const t& t):创建一个vector,元素个数为nSize,且值均为t
vector(const vector&):复制构造函数
vector(begin,end):复制[begin,end)区间内另一个数组的元素到vector中
增加
void push_back(const T& x):向量尾部增加一个元素X
iterator insert(iterator it,const T& x):向量中迭代器指向元素前增加一个元素x
iterator insert(iterator it,int n,const T& x):向量中迭代器指向元素前增加n个相同的元素x
iterator insert(iterator it,const_iterator first,const_iterator last):向量中迭代器指向元素前插入另一个相同类型向量的[first,last)间的数据
删除
iterator erase(iterator it):删除向量中迭代器指向元素
iterator erase(iterator first,iterator last):删除向量中[first,last)中元素
void pop_back():删除向量中最后一个元素
void clear():清空向量中所有元素
赋值
v.assign(n,elem) , 将n个elem的拷贝赋值给v。
v.assign(beg,end) , 将[beg; end)区间中的数据赋值给v。
引用
reference at(int pos):返回pos位置元素的引用
reference front():返回首元素的引用
reference back():返回尾元素的引用
遍历
iterator begin():返回向量头指针,指向第一个元素
iterator end():返回向量尾指针,指向向量最后一个元素的下一个位置
reverse_iterator rbegin():反向迭代器,指向最后一个元素
reverse_iterator rend():反向迭代器,指向第一个元素之前的位置
判断
bool empty() const:判断向量是否为空,若为空,则向量中无元素
大小
int size() const:返回向量中元素的个数
int capacity() const:返回当前向量所能容纳的最大元素值
int max_size() const:返回最大可允许的vector元素数量值
resize 改变当前使用数据的大小,如果它比当前使用的大,则填充默认值
reserve 改变当前vecotr所分配空间的大小即capacity的值
其他
v1.swap(v2):交换两个同类型向量的数据
访问方式
-
数组[] 方式、
-
迭代器
二维数组
vector< vector< int> >v;//这里最外的> >之间要有空格。否则在比较旧的编译器下无法通过
int N=5, M=6;
vector<vector<int> > obj(N); //定义二维动态数组大小5行
for(int i =0; i< obj.size(); i++)//动态二维数组为5行6列,值全为0
{
obj[i].resize(M);
}
for(int i=0; i< obj.size(); i++)//输出二维动态数组
{
for(int j=0;j<obj[i].size();j++)
{
cout<<obj[i][j]<<" ";
}
cout<<"\n";
}
int N=5, M=6;
vector<vector<int> > obj(N, vector<int>(M)); //定义二维动态数组5行6列
for(int i=0; i< obj.size(); i++)//输出二维动态数组
{
for(int j=0;j<obj[i].size();j++)
{
cout<<obj[i][j]<<" ";
}
cout<<"\n";
}
return 0;
顺序容器deque
deque将内存分块,每次分配固定大小的分块,一个分块填充满后开辟新的分块,属于散布-连续混杂的情况
属于局部连续存储结构,类似于vector,不同之处在于, deque支持高效的首/尾端插入/删除操作。
所谓的deque是”double ended queue”的缩写,双端队列不论在尾部或头部插入元素,都十分迅速。而在中间插入元素则会比较费时,因为必须移动中间其他的元素。双端队列是一种随机访问的数据类型,提供了在序列两端快速插入和删除操作的功能,它可以在需要的时候改变自身大小,完成了标准的C++数据结构中队列的所有功能。
Vector是单向开口的连续线性空间,deque则是一种双向开口的连续线性空间。deque对象在队列的两端放置元素和删除元素是高效的,而向量vector只是在插入序列的末尾时操作才是高效的。
deque和vector的最大差异,
- deque允许在常数时间内对头端进行元素的插入或移除操作
- deque没有所谓的capacity观念,因为它是动态地以分段连续空间组合而成,随时可以增加一段新的空间并链接起来。换句话说,像vector那样“因旧空间不足而重新配置一块更大空间,然后复制元素,再释放旧空间”这样的事情在deque中是不会发生的。也因此,deque没有必要提供所谓的空间预留(reserved)功能。
虽然deque也提供Random Access Iterator,但它的迭代器并不是普通指针,其复杂度和vector不可同日而语,这当然涉及到各个运算层面。因此,除非必要,我们应尽可能选择使用vector而非deque。对deque进行的排序操作,为了最高效率,可将deque先完整复制到一个vector身上,将vector排序后(利用STL的sort算法),再复制回deque。
#include<deque>
using namespace std;
构造(同vector)
deque<type> deq; // 声明一个元素类型为type的双端队列que
deque<type> deq(size); // 声明一个类型为type、含有size个默认值初始化元素的的双端队列que
deque<type> deq(size, value); // 声明一个元素类型为type、含有size个value元素的双端队列que
deque<type> deq(mydeque); // deq是mydeque的一个副本
deque<type> deq(first, last); // 使用迭代器first、last范围内的元素初始化deq
引用
deq[ ]:用来访问双向队列中单个的元素。
deq.at(idx):传回索引idx所指的数据,如果idx越界,抛出out_of_range。
deq.front():返回第一个元素的引用。
deq.back():返回最后一个元素的引用。
赋值
deq.assign(beg,end):将[beg; end)区间中的数据赋值给deq。
deq.assign(n,elem):将n个elem的拷贝赋值给deq。
增加
c.insert(it,elem):在pos位置插入一个elem拷贝,传回新数据位置。
c.insert(it,n,elem):在pos位置插入>n个elem数据。无返回值。
c.insert(it,beg,end):在pos位置插入在[beg,end)区间的数据。无返回值。
deq.push_front(x):把元素x插入到双向队列的头部。
deq.push_back(x):把元素x插入到双向队列的尾部。
删除
c.erase(pos):删除pos位置的数据,传回下一个数据的位置。
c.erase(beg,end):删除[beg,end)区间的数据,传回下一个数据的位置。
deq.pop_front():弹出双向队列的第一个元素。
deq.pop_back():弹出双向队列的最后一个元素。
deq.clear();
判断
deq.empty()
大小
c.size():返回容器中实际数据的个数。
c.resize(num):重新指定队列的长度。
c.max_size():返回容器中最大数据的数量。
其他
c.get_allocator:使用构造函数返回一个拷贝
C1.swap(c2):将c1和c2元素互换。
deque的一些特点:
- 支持随机访问,即支持[ ]以及at(),但是性能没有vector好。
- 可以在内部进行插入和删除操作,但性能不及list。
- deque两端都能够快速插入和删除元素,而vector只能在尾端进行。
- deque的元素存取和迭代器操作会稍微慢一些,因为deque的内部结构会多一个间接过程。
- deque迭代器是特殊的智能指针,而不是一般指针,它需要在不同的区块之间跳转。
- deque可以包含更多的元素,其max_size可能更大,因为不止使用一块内存。
- deque不支持对容量和内存分配时机的控制。
- deque不提供容量操作:capacity()和reverse(),但是vector可以。
- 在除了首尾两端的其他地方插入和删除元素,都将会导致指向deque元素的任何pointers、references、iterators失效。不过,deque的内存重分配优于vector,因为其内部结构显示不需要复制所有元素。
- deque的内存区块不再被使用时,会被释放,deque的内存大小是可缩减的。不过,是不是这么做以及怎么做由实际操作版本定义。
顺序容器list
List是stl实现的双向链表,与向量(vectors)相比, 它允许快速的插入和删除,但是随机访问却比较慢。 list不支持下标访问和at访问
非连续存储结构,具有双链表结构,每个元素维护一对前向和后向指针,因此支持前向/后向遍历。 支持高效的随机插入/删除操作,但随机访问效率低下,且由于需要额外维护指针 ,开销也比较大
#include<list>
using namespace std;
构造(同vector)
list<int>lst1; //创建空list
list<int> lst2(5); //创建含有5个元素的list
list<int>lst3(3,2); //创建含有3个元素的list
list<int>lst4(lst2); //使用lst2初始化lst4
list<int>lst5(lst2.begin(),lst2.end()); //同lst4
引用
Lst1.front() 返回第一个元素
Lst1.back() 返回最后一个元素
遍历
Lst1.begin() 返回指向第一个元素的迭代器
Lst1.end() 返回末尾的迭代器
Lst1.rbegin() 返回指向第一个元素的逆向迭代器
Lst1.rend() 指向list末尾的逆向迭代器
增加
Lst1.push_front() 在list的头部添加一个元素
Lst1.push_back() 在list的末尾添加一个元素
Lst1.insert() 插入一个元素到list中
删除
Lst1.pop_front() 删除第一个元素
Lst1.pop_back() 删除最后一个元素
Lst1.erase() 删除一个元素
Lst1.clear() 删除所有元素
Lst1.unique() 删除list中相邻重复的元素
Lst1.remove(elem); remove和erase都是删除一个元素,其中remove参数类型和数据类型一致,而erase参数类型是迭代器。若有多个elem,都会删除
Lst1.remove_if(bool func(…)) 按指定条件删除元素 接受一个函数符作为参数,应用于list中的每个成员,如果返回true,则删除该元素
大小
Lst1.size() 返回list中的元素个数
Lst1.resize() 改变list的大小
Lst1.max_size() 返回list能容纳的最大元素数量
赋值
Lst1.assign(begin, end) 给list赋值
Lst1.assign(n, elem)
判断
Lst1.empty() 如果list是空的则返回true
splice()和merge()
Lst1.splice(iterator position, list& x) 合并两个list
list1.splice(it1, list2); //将list2中的所有元素拷贝到list1中。在list1中的起始位置是it1.复制结束后,list2将为空。
Lst1.splice(iterator position, list& x, iterator i) 合并两个list
list1.splice(it1, list2, it2); //这个功能是将list2中的元素,从it2开始,剪切到list1的it1起始的地方。
Lst1.splice(iterator position, list& x, iterator first, iterator last) 合并两个list
list1.splice(it1, list2, it2begin, it2end);
Lst1.merge() 合并两个list
list1.merge(list2); //注意:在使用merge之前,必须使list1和list2已经排好顺序。并且,合并之后list1仍然是有序的。
其他
Lst1.reverse(void) 把list的元素倒转
Lst1.sort(void) 给list排序
Lst1.swap(Lst2) 交换两个list
Lst1.get_allocator() 返回list的配置器
示例
list相关函数代码示例: https://www.cnblogs.com/nuosenli/articles/11362864.html
vector,list和deque区别
stl提供了三个最基本的容器:vector,list,deque。
- vector拥有一段连续的内存空间,并且起始地址不变,因此它能非常好的支持随即存取,即[]操作符,但由于它的内存空间是连续的,所以在中间进行插入和删除会造成内存块的拷贝,另外,当该数组后的内存空间不够时,需要重新申请一块足够大的内存并进行内存的拷贝。这些都大大影响了vector的效率。
- list就是数据结构中的双向链表,因此它的内存空间可以是不连续的,通过指针来进行数据的访问,这个特点使得它的随即存取变的非常没有效率,因此它没有提供[]操作符的重载。但由于链表的特点,它可以以很好的效率支持任意地方的删除和插入。
- deque支持[]操作符,也就是支持随即存取,并且和vector的效率相差无几,它支持在两端的操作:push_back,push_front,pop_back,pop_front等,并且在两端操作上与list的效率也差不多。
因此在实际使用时,如何选择这三个容器中哪一个,应根据你的需要而定,一般应遵循下面的原则:
1、如果你需要高效的随即存取,而不在乎插入和删除的效率,使用vector
2、如果你需要大量的插入和删除,而不关心随即存取,则应使用list
3、如果你需要随即存取,而且关心两端数据的插入和删除,则应使用deque。
vector中的元素随机访问效率很高。
在vecotor中插入或者删除某个元素,需要将现有元素进行复制,移动。如果vector中存储的对象很大,或者构造函数复杂,则在对现有元素进行拷贝时开销较大,因为拷贝对象要调用拷贝构造函数。对于简单的小对象,vector的效率优于list。
关联容器map
#include<map>
using namespace std;
map的键值key不可重复,map支持[ ]运算符
map是字典的抽象数据结构。
map中的所有的元素都会根据key自动进行排序,而且所有的元素都是唯一的。map中的所有的元素都是pair,即键(key)和值(value)组成的序列(pair中的第一个元素为key,第二个元素为value)。
typedef std::map<int,string> EMPLOYEE_MAP;
EMPLOYEE_MAP employees;
employees.insert(EMPLOYEE_MAP::value_type(25301, "A"));
employees.insert(EMPLOYEE_MAP::value_type(25302, "B"));
employees.insert(EMPLOYEE_MAP::value_type(25303, "C"));
employees.insert(EMPLOYEE_MAP::value_type(25304, "D"));
employees.insert(EMPLOYEE_MAP::value_type(25305, "E"));
EMPLOYEE_MAP::iterator itEnd = employees.end();
for (EMPLOYEE_MAP::iterator it = employees.begin(); it != itEnd; ++it)
{
std::cout << it->first << "-";
std::cout << it->second << endl;
}
输出结果
25301-A
25302-B
25303-C
25304-D
25305-E
声明
map<int, string> ID_Name;
基本操作函数
begin() 返回指向map头部的迭代器
end() 返回指向map末尾的迭代器
rbegin() 返回一个指向map尾部的逆向迭代器
rend() 返回一个指向map头部的逆向迭代器
insert() 插入元素
count() 返回指定元素出现的次数
erase() 删除一个元素
clear() 删除所有元素
empty() 如果map为空则返回true
find() 查找一个元素
size() 返回map中元素的个数
max_size() 返回可以容纳的最大元素个数
------------分割线-----------
key_comp() 返回比较元素key的函数
value_comp() 返回比较元素value的函数
lower_bound() 返回键值>=给定元素的第一个位置
upper_bound() 返回键值>给定元素的第一个位置
equal_range() 返回特殊条目的迭代器对
get_allocator() 返回map的配置器
swap() 交换两个map
迭代器
共有八个获取迭代器的函数:begin, end, rbegin,rend 以及对应的 cbegin, cend, crbegin,crend。
二者的区别在于,后者一定返回 const_iterator,而前者则根据map的类型返回iterator 或者 const_iterator。
插入操作
用insert插入pair数据
map<int, string> mapStudent;
mapStudent.insert(pair<int, string>(1, "student_one"));
mapStudent.insert(pair<int, string>(2, "student_two"));
mapStudent.insert(pair<int, string>(3, "student_three"));
map<int, string>::iterator iter;
for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)
cout<<iter->first<<' '<<iter->second<<endl;
用insert函数插入value_type数据
map<int, string> mapStudent;
mapStudent.insert(map<int, string>::value_type (1, "student_one"));
mapStudent.insert(map<int, string>::value_type (2, "student_two"));
mapStudent.insert(map<int, string>::value_type (3, "student_three"));
map<int, string>::iterator iter;
for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)
cout<<iter->first<<' '<<iter->second<<endl;
用insert函数进行多个插入
insert共有4个重载函数:
- 插入单个键值对,并返回插入位置和成功标志,插入位置已经存在值时,插入失败
pair<iterator,bool> insert (const value_type& val);
- 在指定位置插入,在不同位置插入效率是不一样的,因为涉及到重排
iterator insert (const_iterator position, const value_type& val);
- 插入多个
void insert (InputIterator first, InputIterator last);
- c++11开始支持,使用列表插入多个
void insert (initializer_list<value_type> il);
示例
#include <iostream>
#include <map>
int main()
{
std::map<char, int> mymap;
// 插入单个值
mymap.insert(std::pair<char, int>('a', 100));
mymap.insert(std::pair<char, int>('z', 200));
//返回插入位置以及是否插入成功
std::pair<std::map<char, int>::iterator, bool> ret;
ret = mymap.insert(std::pair<char, int>('z', 500));
if (ret.second == false) {
std::cout << "element 'z' already existed";
std::cout << " with a value of " << ret.first->second << '\n';
}
//指定位置插入
std::map<char, int>::iterator it = mymap.begin();
mymap.insert(it, std::pair<char, int>('b', 300)); //效率更高
mymap.insert(it, std::pair<char, int>('c', 400)); //效率非最高
//范围多值插入
std::map<char, int> anothermap;
anothermap.insert(mymap.begin(), mymap.find('c'));
// 列表形式插入
anothermap.insert({ { 'd', 100 }, {'e', 200} });
return 0;
}
//第三种:用数组方式插入数据,下面举例说明
#include <map>
#include <string>
#include <iostream>
using namespace std;
int main()
{
map<int, string> mapStudent;
mapStudent[1] = "student_one";
mapStudent[2] = "student_two";
mapStudent[3] = "student_three";
map<int, string>::iterator iter;
for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)
cout<<iter->first<<' '<<iter->second<<endl;
}
以上三种用法,虽然都可以实现数据的插入,但是它们是有区别的,当然了第一种和第二种在效果上是完成一样的。
用insert函数插入数据,在数据的插入上涉及到集合的唯一性这个概念,即当map中有这个关键字时,insert操作是插入数据不了的,但是用数组方式就不同了,它可以覆盖以前该关键字对应的值
用程序说明:
mapStudent.insert(map<int, string>::value_type (1, “student_one”));
mapStudent.insert(map<int, string>::value_type (1, “student_two”));
上面这两条语句执行后,map中1这个关键字对应的值是“student_one”,第二条语句并没有生效,那么这就涉及到我们怎么知道insert语句是否插入成功的问题了,可以用pair来获得是否插入成功,
程序如下
pair<map<int, string>::iterator, bool> Insert_Pair;
Insert_Pair = mapStudent.insert(map<int, string>::value_type (1, “student_one”));
我们通过pair的第二个变量来知道是否插入成功,它的第一个变量返回的是一个map的迭代器,如果插入成功的话Insert_Pair.second应该是true的,否则为false。
查找
iterator find (const key_type& k);
const_iterator find (const key_type& k) const;
删除
//删除迭代器指向位置的键值对,并返回一个指向下一元素的迭代器
iterator erase( iterator pos )
//删除一定范围内的元素,并返回一个指向下一元素的迭代器
iterator erase( const_iterator first, const_iterator last );
//根据Key来进行删除, 返回删除的元素数量,在map里结果非0即1
size_t erase( const key_type& key );
//清空map,清空后的size为0
void clear();
交换
//就是两个map的内容互换
void swap( map& other );
容量
//查询
bool empty();
//查询map中键值对的数量
size_t size();
//查询map所能包含的最大键值对数量,和系统和应用库有关。
//注意:这并不意味着用户一定可以存这么多,很可能还没达到就已经开辟内存失败了
size_t max_size();
//查询关键字为key的元素的个数,在map里结果非0即1
size_t count( const Key& key ) const;
扩展:unordered_map
在c++11标准前,c++标准库中只有一种map,但是它的底层实现并不是适合所有的场景,如果我们需要其他适合的map实现就不得不使用比如boost库等三方的实现,在c++11中加了一种map,即: unordered_map,unordered_set,他们的实现有什么不同呢?
-
unordered_map的底层采用哈希表的实现,查询的时间复杂度为是O(1)。所以unordered_map内部就是无序的,数据是按散列函数插入到槽里面去的,数据之间无顺序可言,如果我们不需要内部有序,就可以使用unordered_map。unordered_map也是属于关联式容器,采用std::pair保存key-value形式的数据。用法与map一致。
-
map底层采用的是红黑树的实现查询的时间复杂度为O(logn),看起来并没有unordered_map快,但是也要看实际的数据量,虽然unordered_map的查询从算法上分析比map快,但是它有一些其它的消耗,比如哈希函数的构造和分析,还有如果出现哈希冲突解决哈希冲突等等都有一定的消耗,因此unordered_map的效率在很大的程度上由它的hash函数算法决定,而红黑树的效率是一个稳定值。
unordered_map用hash结构会有一定的内存损失,它的内存占用高于map,但是效率会更高。
关于自定义类型的重载:
STL中的map因为是有序的二叉树存储,所以对key值需要有大小的判断,当使用内置类型时,无需重载operator < ;但是用用户自定义类型的话,就需要重载operator < 。 unoredered_map全程使用不需要比较元素的key值的大小,但是,对于元素的==要有判断,又因为需要使用hash映射,所以,对于非内部类型,需要程序员为其定义这二者的内容,对于内部类型,就不需要了。
unordered库使用“桶”来存储元素,散列值相同的被存储在一个桶里。当散列容器中有大量数据时,同一个桶里的数据也会增多,造成访问冲突,降低性能。为了提高散列容器的性能,unordered库会在插入元素时自动增加桶的数量,不需要用户指定。但是,用户也可以在构造函数或者rehash()函数中,指定最小的桶的数量。个人理解: 此处说的“散列值相同的被存储在一个桶里”, 是不是指unordered_multimap, 因为unordered_map的key不可重复,个人猜测此处的“散列值相同”指的是key。
总结: 首先如果你需要对map中的数据排序,就首选map,他会把你的数据按照key的自然排序排序(由于它的底层实现红黑树机制所以会排序),如果不需要排序,就看你对内存和效率的选择了,unordered_map会比map多占用内存,但是效率会更高。
至于使用方法和函数,两者差不多。unordered_multimap用法亦可类推。
关联容器multimap
#include<map>
using namespace std;
multimap的键值key可以重复
也正是由于这种区别,map支持[ ]运算符,multimap不支持[ ]运算符。在用法上没什么区别。
关联容器set
#include<set>
using namespace std;
set的所有的元素都是有序的而且set中所有的元素都是唯一的;
set中的元素即是键值又是实值
set<string> fruits;
fruits.insert("apple");
fruits.insert("orange");
fruits.insert("banana");
set<string>::iterator itEnd = fruits.end();
for (set<string>::iterator it=fruits.begin(); it != itEnd; it++)
cout << *it << " ";
输出结果
apple banana orange
不能通过set的迭代器去修改set元素,原因是修改元素会破坏set组织。
当对容器中的元素进行插入或者删除时,操作之前的所有迭代器在操作之后依然有效。
由于set元素是排好序的,且默认为升序,因此当set集合中的元素为结构体或自定义类时,该结构体或自定义类必须实现运算符<的重载。
常用成员函数
begin()–返回指向第一个元素的迭代器
end()–返回指向最后一个元素的迭代器
rbegin()–返回指向集合中最后一个元素的反向迭代器
rend()–返回指向集合中第一个元素的反向迭代器
insert()–在集合中插入元素
find()–返回一个指向被查找到元素的迭代器
erase()–删除集合中的元素
clear()–清除所有元素
count()–返回某个值元素的个数
empty()–如果集合为空,返回true
max_size()–返回集合能容纳的元素的最大限值
size()–集合中元素的数目
swap()–交换两个集合变量
get_allocator()–返回集合的分配器
lower_bound()–返回指向大于(或等于)某值的第一个元素的迭代器
upper_bound()–返回大于某个值元素的迭代器
key_comp()–返回一个用于元素间值比较的函数
value_comp()–返回一个用于比较元素间的值的函数
equal_range()–返回集合中与给定值相等的上下限的两个迭代器
示例
set容器中,元素类型为基本类型,如何让set按照用户意愿来排序?
set容器中,如何让元素类型为自定义类型? 注意: 此处为什么没有重载 < , 是因为使用了仿函数
set容器的insert函数的返回值为什么类型?
#include <iostream>
#include <string>
#include <set>
using namespace std;
void test01()
{
//set容器默认从小到大排序
set<int> s;
s.insert(10);
s.insert(20);
s.insert(30);
//输出set
PrintSet(s);
//结果为:10 20 30
/* set的insert函数返回值为一个对组(pair)。
对组的第一个值first为set类型的迭代器:
1、若插入成功,迭代器指向该元素。
2、若插入失败,迭代器指向之前已经存在的元素
对组的第二个值seconde为bool类型:
1、若插入成功,bool值为true
2、若插入失败,bool值为false
*/
pair<set<int>::iterator, bool> ret = s.insert(40);
if (true == ret.second)
cout << *ret.first << " 插入成功" << endl;
else
cout << *ret.first << " 插入失败" << endl;
}
void test02()
{
/* 如果想让set容器从大到小排序,需要给set容
器提供一个仿函数,本例的仿函数为CompareSet
*/
set<int, CompareSet> s;
s.insert(10);
s.insert(20);
s.insert(30);
//打印set
PrintSet(s);
//结果为:30,20,10
}
void test03()
{
/* set元素类型为Person,当set元素类型为自定义类型的时候
必须给set提供一个仿函数,用于比较自定义类型的大小,
否则无法通过编译
*/
set<Person,ComparePerson> s;
s.insert(Person("John", 22));
s.insert(Person("Peter", 25));
s.insert(Person("Marry", 18));
s.insert(Person("Peter", 36));
//打印set
PrintSet(s);
}
int main(void)
{
//test01();
//test02();
//test03();
return 0;
}
///
///
///
/* 仿函数CompareSet,在test02使用 */
class CompareSet
{
public:
//从大到小排序
bool operator()(int v1, int v2)
{
return v1 > v2;
}
//从小到大排序
//bool operator()(int v1, int v2)
//{
// return v1 < v2;
//}
};
///
///
///
/* Person类,用于test03 */
class Person
{
friend ostream &operator<<(ostream &out, const Person &person);
public:
Person(string name, int age)
{
mName = name;
mAge = age;
}
public:
string mName;
int mAge;
};
ostream &operator<<(ostream &out, const Person &person)
{
out << "name:" << person.mName << " age:" << person.mAge << endl;
return out;
}
///
///
///
/* 仿函数ComparePerson,用于test03 */
class ComparePerson
{
public:
//名字大的在前面,如果名字相同,年龄大的排前面
bool operator()(const Person &p1, const Person &p2)
{
if (p1.mName == p2.mName)
{
return p1.mAge > p2.mAge;
}
return p1.mName > p2.mName;
}
};
///
///
///
/* 打印set类型的函数模板 */
template<typename T>
void PrintSet(T &s)
{
for (T::iterator iter = s.begin(); iter != s.end(); ++iter)
cout << *iter << " ";
cout << endl;
}
关联容器multiset
#include<set>
using namespace std;
multiset和set基本相同,所不同的是,一个multiset中的元素是可以重复的。
set和multiset的底层实现是一种高效的平衡二叉树,即红黑树(Red-Black Tree)。
multiset容器的insert函数返回值为什么?
multiset的insert函数返回值为multiset类型的迭代器,
指向新插入的元素。multiset允许插入相同的值,因此
插入一定成功,因此不需要返回bool类型。
容器共性
容器都是物之所在,这就决定了它们必然存在很多共性,这些共性包括迭代器、大小等属性。容器与容器之间的主要区别体现在对数据的操作上。
每类容器都包含四个迭代器:iterator(正向迭代器)、const_iterator(常正向迭代器)、reverse_iterator(反向迭代器)、const_reverse_iterator(常反向迭代器)。因此你可以按照下面的方式获取每个容器的相应的迭代器:
获取正向迭代器
C<T>::iterator it = c.begin();
C <T>::iterator it = c.end();
获取反向迭代器
C <T>::reverse_iterator it = c.rbegin();
C <T>:: reverse_iterator it = c.rend()
获取常正向迭代器
C<T>::const_iterator it = c.begin();
C <T>:: const_iterator it = c.end();
获取常反向迭代器
C <T>:: const_ reverse_iterator it = c.rbegin();
C <T>:: const_ reverse_iterator it = c.rend()
注:C为容器类型,c为C的实例
所有容器是数据的存在之处,可以看作的是数据的集合,因此它们都会有大小、是否为空等属性,因此你可以按照下面的方式获取所有的容器的公共属性:
获取容器的大小
c.size();
判断容器是否为空
c.empty();
顺序容器共性
c.push_back
c.pop_back
c.push_front
c.pop_front
c.back
c.front
c.erase
c.remove
capacity、max_size属性
-
capacity属性,表示STL在发生realloc前能允许的最大元素数,也可以理解为预分配的内存空间。例如一个vector<int> v的capacity为5,当插入第6个元素时,vector会realloc,vector内部数据会复制到另外一个内存区域。这样之前指向vector中的元素的指针、迭代器等等均会失效。
-
max_size属性和capacity不同,表示STL容器允许的最大元素数,通常,这个数是一个很大的常整数,可以理解为无穷大。这个数目与平台和实现有关
并不是所有的容器都会发生realloc,List,Map/Multimap,Set/Multiset的元素在内存中散布,不预分配内存,所以不会产生realloc的情况,对于这些容器,其capacity是无意义的,所以这些容器没有capacity()成员函数,也没有capacity属性。
deque双向队列情况比较特殊,deque将内存分块,每次分配固定大小的分块,一个分块填充满后开辟新的分块,也属于散布-连续混杂的情况,虽然deque会预分配内存空间,但也不会产生realloc(人家是alloc),所以也不具有capacity属性。
实际具有capacity属性的容器只有vector、string、 basic_string
- vector<T>
默认构造函数 : capacity = 0
使用构造函数vector(n, value=T()) ,capacity = n
- string (basic_string )
默认构造函数 :capacity = 15
没有指定capacity的构造式
指定了初始字符串的:capacity = 大于字符串长度且等于n*15-1
- basic_string<wchar_t>
默认构造函数:capacity = 7
指定了初始字符串的: capacity = 大于字符串长度且等于n*8-1
除了通过构造式设定capacity,也可以使用reserve(n)来设定容器对象的capacity,避免之后的realloc。不过这一过程是只增不减的,如果n小于当前capacity,则reserve(n)无效。