参考了http://www.cplusplus.com,对网站上的内容有所精简,例程是自己给出的。
这些容器有许多相似的地方,比如迭代器begin()都是指向第一个元素,end()指向最后一个元素的下一个位置,涉及到区间时都是半开半闭(左闭右开或者左开右闭)。又比如初始化时一般都有以下方式(这里以list为例):
/******各种初始化******/
list<int> ex1;// 默认初始化为一个空链表
list<int> ex2(10);// 创建一个含有10个默认值是0的列表
list<int> ex3(10,2);// 创建一个含有10个默认值是2的列表
list<int> ex4(ex3);// 用ex3初始化ex4
list<int> ex5(ex4.begin(),ex4.end());// 使用区间元素初始化
list<int> ex6={1,2,3,4,5,6};//使用列表初始化
这些相同的地方接下来就不一一讨论了。
1、list
(1)简介
C++中的list是一个双向链表(doubly-linked list)。它的插入和删除均为常数时间O(1),但是不支持对元素的直接访问(即使用[]来访问元素),同时使用list也需要额外的内存开销。
(2)常用操作
a、迭代器
/******迭代器******/
list<int>:: iterator iter1,iter2; // 申明(正向)迭代器
list<int>:: reverse_iterator iter3,iter4;// 申明反向迭代器
iter1=ex6.begin();// 指向第一个元素
iter2=ex6.end();// 指向最后一个元素的下一个位置
iter3=ex6.rbegin();// 指向最后一个元素
iter4=ex6.rend();// 指向第一个元素的前一个位置
//cout<<*(iter1+1)<<endl;// 错误!这是一个链表,只能顺序访问!但是list的迭代器可以++和--
需要注意的地方是list只能顺序访问,所以对迭代器进行加减是不允许的。
此外还有cbegin(),cend(),crbegin(),crend(),这和const关键字有关,这里不展开。
b、容量
empty()函数测试list是不是空的;
size()函数返回大小;
max_size()返回list所能存储的最大大小,所有的list都会返回一个相同的值,很大很大(768614336404564650)。
c、元素访问
/******元素访问******/
cout<<ex6.front()<<endl;// 用front()函数访问第一个元素
cout<<ex6.back()<<endl;// 用back()函数访问最后一个元素
d、修改
/******修改******/
ex6.assign(2,7);// assign的作用是重新赋值,例如这行代码给ex6赋值2个7
ex6.assign(ex3.begin(),ex3.end());// 给ex6赋值这个范围的元素
ex6.emplace_front(1);// 在最前面插入1
ex6.emplace_back(3);// 在最后面插入3
ex6.emplace(ex6.begin(),0);// 第一个参数是迭代器,是插入的位置,后面是插入的数据,例如这里插入了0
ex6.emplace(ex6.end(),9);// 在最后插入9
ex6.push_back(233);// 往末尾push一个元素
ex6.pop_back();// pop末尾元素
ex6.push_front(222);// 往前面push一个元素
ex6.pop_front(); // pop前面元素
list<int>:: iterator it=ex6.begin();
it++;// ex6中的元素是0 1 2 2 2 2 2 2 2 2 2 2 3 9, it指向1
ex6.insert(it,10);// 10插到了1前面,it仍然指向1
ex6.insert(it,2,9);// 在1前面插入两个9
list<int> ex7={-1,-1};
ex6.insert(it,ex7.begin(),ex7.end());// 用迭代器来插入,只要是迭代器都可以,例如可以用vector
list<int> ex8={10,20,30,40,50,60,70,80,90};
list<int>:: iterator it1,it2;
it1=it2=ex8.begin();// 10,20,30,40,50,60,70,80,90
// ^^
advance(it2,7);// 10,20,30,40,50,60,70,80,90
// ^ ^
ex8.erase(it2);// 10,20,30,40,50,60,70,90
// ^ ^
it1++;
it2--;// 10,20,30,40,50,60,70,,90
// ^ ^
ex8.erase(it1,it2);// 10,70,90
// ^^
ex6.swap(ex8);// 交换两个链表的内容
ex6.resize(5);// 改变容器大小,ex6的内容是10,70,90,现在变成10,70,90,0,0
ex6.resize(7,2);// 现在变成10,70,90,0,0,2,2
ex6.resize(0);// 现在什么都没了,若调用size()函数会得到0
ex6.clear();// 将ex6的内容全清空
e、其他
#include<iostream>
#include<list>
using namespace std;
bool predicate1(const int& val){return (val>=30);}
bool predicate2(const int& val1,const int& val2){return abs(val1-val2)<=1;}
bool cmp(double val1,double val2){/*do something here...*/}
int main(int argc, char* argv[])
{
list<int> mylist1={1,2,3,4,5};
list<int> mylist2={10,20,30};
list<int>::iterator it=mylist1.begin();
mylist1.splice(it,mylist2);// 第一个参数是插入位置,第二个参数是插过来的list
// mylist1变成10,20,30,1,2,3,4,5
// mylist2变为空
mylist2.splice(mylist2.begin(),mylist1,it);// 还可以有第三个参数,指向要插入的元素
// mylist2变成1
// 指向1的迭代器it失效了!不能再用!
mylist2.splice(mylist2.begin(),mylist1,mylist1.begin(),mylist1.end());
list<int> mylist3={10,20,30,40,50};
mylist3.remove(40);// 从mylist3里删除40
mylist3.remove_if(predicate1);// 删除满足条件的元素,这个条件可以是一个函数或者类
// 例如这里删掉了大于等于30的元素,剩下10和20
list<int> mylist4={1,2,3,4,4,4,5,6,7,9,9};
mylist4.unique();// 1,2,3,4,5,6,7,9
mylist4.unique(predicate2);// unique可以传入predicate
// 变成1,3,5,7,9
list<double> mylist5={1.2,0.9,3,14,0.3};
list<double> mylist6={20.3,1.1,0.25};
//mylist5.merge(mylist6);// 直接merge,变成1.2,0.9,3,14,0.3,20.3,1.1,0.25
mylist5.sort();// list排序,排序函数可以自己定义,默认升序
mylist6.sort();
mylist5.merge(mylist6);// mylist5全部升序排好,mylist6变成空的
list<double> mylist7={1.2,0.9,3,14,0.3};
list<double> mylist8={20.3,1.1,0.25};
mylist7.sort();
mylist8.sort();
mylist7.merge(mylist8,cmp);
mylist7.reverse();// 反转所有元素
for(auto i : mylist7)
cout<<i<<" ";
}
2、forward_list
(1)简介
类似list,但是这是单链表,只能支持一个方向的操作(因此,对于迭代器,只能++,不能--)。
(2)常用操作
a、迭代器
注意有一个特有的before_begin():
forward_list<int> ex1={1,2,3};
forward_list<int>::iterator it1,it2;
it1=ex1.begin();
it2=ex1.before_begin();// 返回的迭代器指向第一个元素的前一个位置
//ex1.insert_after(it1,9);// 1,9,2,3
ex1.insert_after(it2,9);// 9,1,2,3
for(auto i : ex1)
cout<<i<<" ";
b、容量
只有empty()和max_size(),没有size()。
c、元素访问
只有front()。
d、修改
类似list,但是只能单向操作:
(具体怎么用可以类比list,略)
e、其他
(具体怎么用可以类比list,略)
3、array(C++11新增)
(1)简介
就是我们熟悉的数组。定长的容器。
(2)常用操作
a、迭代器
同list
b、容量
同list
c、元素访问
array<int,7> ex1={2,5,7,9};
cout<<ex1[4]<<endl;// 使用[]访问
cout<<ex1.at(1)<<endl;// 使用at访问
cout<<ex1.front()<<endl;
cout<<ex1.back()<<endl;
cout<<*(ex1.data()+2)<<endl;// data()返回一个指向第一个元素的指针,这里将打印7
d、修改
array<int,8> ex2;
ex2.fill(3);// 全部填充3
array<int,8> ex3={1,2,3,4,5,6,7,8};
ex2.swap(ex3);// ex1和ex2交换,两者大小要一样!
e、其他
get,relational operators暂略。
4、deque
(1)简介
数据结构中学过的“双端队列”(double-ended queue),即在首尾两端都允许出队和入队的队列。这个容器和vector很相似,但是对deque而言,元素在内存中并不是线性存放的(元素可能分布在不同位置),因此,采用(指针+offset)的方式去访问deque中的元素是未定义行为。
(2)常用操作
a、迭代器
同list
b、容量
特殊的就是最后一个shrink_to_fit,作用是减小deque的内存占用以适应它当前的大小(因为deque往往占用了更多自己用不完的内存),一般在resize()完以后用这个函数。
c、元素访问
d、修改
e、其他
略
5、queue
(1)简介
定义在头文件<queue>中,是FIFO queue。
(2)常用操作
这个容器比较简单,类成员函数只有:
6、priority_queue
(1)简介
和queue一起定义在头文件<queue>中,是优先队列。它的第一个元素总是最大的(和“最大堆”和相似)。
(2)常用操作
同样比较简单:
#include<iostream>
#include<queue>
using namespace std;
int main(int argc, char* argv[])
{
priority_queue<int> ex1;
ex1.push(3);
ex1.push(100);
cout<<ex1.top(); //得到100
}
不能对优先队列进行遍历,每次只能取第一个元素。
7、stack
(1)简介
大名鼎鼎的“栈”。
(2)常用操作
8、map
(1)简介
map是一种关联容器,其中的元素可以看作是一个二元组,第一个分量叫key,第二个叫value。map最大的特点是有序性,容器中的元素会按照key以字典序排好。
(2)常用操作
a、迭代器
同list
b、容量
同list
c、元素访问
[]和at(),用法都一样。
map<string,int> ex1={{"bob",100},{"jack",90},{"tom",95}};
cout<<ex1.at("bob")<<endl; //打印100
cout<<ex1["bob"]<<endl;// 打印100
d、修改
map<string,int> ex1={{"bob",100},{"jack",90},{"tom",95}};
//cout<<ex1.at("bob")<<endl; // 打印100
//cout<<ex1["bob"]<<endl;// 打印100
ex1.insert(pair<string,int>("john",50));// john会插到jack后面
map<string,int>::iterator it1=ex1.begin();
it1++;
ex1.insert(it1,pair<string,int>("adam",60));// adam还是会跑到第一个
map<string,int>::iterator it2,it3;
it2=ex1.find("jack");// 返回一个指向这个元组的迭代器
ex1.erase(it2);// 删除
ex1.erase(++it2,ex1.end());// 按范围删除
ex1.emplace("james",32);// emplace插入
it3=ex1.emplace_hint("wood",79);// emplace_hint
for(auto& i : ex1)
cout<<i.second<<" ";
e、其他
#include<iostream>
#include<map>
#include<string>
using namespace std;
int main(int argc, char* argv[])
{
map<int,int> mymap={{1,100},{2,90},{3,95},{4,30},{5,60}};
map<int,int>::key_compare mycomp=mymap.key_comp();
int largest=mymap.rbegin()->first;// 得到5
for(auto it1=mymap.begin();mycomp((*it1).first,largest);it1++)// 将key小于5的都打印出来
cout<<it1->second<<" ";
map<int,int>::value_compare val_cmp=mymap.value_comp();
pair<int,int> val=*mymap.rbegin();
for(auto it2=mymap.begin();val_cmp((*it2),val);it2++)// 当it2指向的pair的key排在val的key前面时返回true
cout<<it2->second<<" ";
}
9、multimap
(1)简介
和map一样,但是允许多个元素的key一样。
(2)常用操作
和map差不多,多出来的几个:
count():计算相同key的pair有几个,剩下的看代码:
multimap<int,int> mymultimap={{1,100},{1,90},{1,95},{2,30},{3,60},{3,88}};
multimap<int,int>::iterator itlow,itup;
itlow=mymultimap.lower_bound(1);// 返回迭代器,这里会指向第一个
itup=mymultimap.upper_bound(3);// 会指向第二个key是3的
for(auto it=itlow;it!=itup;++it)
cout<<(*it).second<<" ";// 所以结果是全部输出来
pair<multimap<int,int>::iterator,multimap<int,int>::iterator> ret; // equal_range会返回含有迭代器的pair
// 第一个是range开头,第二个是
ret=mymultimap.equal_range(3);
for(auto it=ret.first;it!=ret.second;++it)
cout<<it->second<<" ";// 输出60 88
10、unordered_map(C++11新增)
(1)简介
顾名思义,不需要管顺序的map。这就是C++中的哈希表了。一个unordered_map由许多个bucket组成,每个bucket是它内部的一个槽(slot),一个简单的示意图:
(2)常用操作
a、容量
b、迭代器
c、元素访问
[]和at()。
d、修改
e、桶(buckets)
第一个返回有几个bucket,第二个返回最多能有几个bucket,第三个返回每个桶里装的元素个数,最后一个需要传入迭代器的first,返回桶的编号。
f、哈希
第一个返回load factor,这个东西的定义是:所有元素的个数/桶的个数;第二个返回可能的最大load factor;第三个的用法是rehash(int n),将会改变哈希表的大小,得到n个或者更多的桶,如果n比当前的桶数小这个函数没有效果;最后一个的用法是reserve(int n),可以把调整哈希表大小,使得桶数等于n。
g、其他
剩下这三个不会怎么用到(比如第一个是返回哈希函数的......),略。
11、unordered_multimap(C++11新增)
(1)简介
在unordered_map的基础上,允许key重复
(2)常用操作
类似unordered_map,略。
12、set
(1)简介
“集合”。储存的元素互不相同,元素就是自己的key,同样会按照key的字典序排列。
(2)常用操作
set<double> ex1;
ex1.insert(3.14);
ex1.insert(0);
ex1.insert(5.9);
ex1.insert(0);// 无效的insert
for(auto i : ex1)
cout<<i<<" ";// 输出0 3.14 5.9
救命我好累......
13、multi-set
(1)简介
允许有重复的值。
(2)常用操作
同set,略。
14、unordered_set(C++11新增)
(1)简介
内部也是一个hash table,但是由于key就是value,所以可以看成是“塌缩”的unordered_map。
(2)常用操作
15、unordered_multiset(C++11新增)
(1)简介
允许有重复的值。
(2)常用操作
略。
16、vector
(1)简介
动态数组。老朋友了!
(2)常用操作
呼......总算(虎头蛇尾地)写完了。算是对C++的容器进行了一次全面(cu cao)的了解,留着以后慢慢补充了!