C++的容器总结

本文介绍了C++标准库中的几种容器,包括list、forward_list、array、deque、queue、priority_queue、stack、map、multimap、unordered_map、unordered_multimap、set、multi-set、unordered_set、unordered_multiset和vector,详细阐述了它们的特点、常用操作和适用场景,如容器的容量、元素访问、修改等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考了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)的了解,留着以后慢慢补充了!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值