小言_互联网的博客

C++中STL容器总结

455人阅读  评论(0)


整理博客不易,如果本文对你有所帮助,请给博主 点赞 + 收藏 + 关注!谢谢!

1、容器分类

STL中通常将容器分为三类:顺序容器关联容器容器适配器
1、顺序容器

是一种各元素之间有顺序关系的线性表,是一种线性结构的可序群集。顺序性容器中的每个元素均有固定的位置,除非用删除或插入的操作改变这个位置。顺序容器的元素排列次序与元素值无关,而是由元素添加到容器里的次序决定。

顺序容器包括:

vector(向量)、list(列表)、deque(队列)。

2、关联容器

关联式容器是非线性的树结构,更准确的说是二叉树结构。排序的容器底层是通过红黑树实现的,散列的是通过哈希表实现的。

关联容器可分为两类:

(1)有序的:map(集合)、set(映射)、multimap(多重集合)、multiset(多重映射)。
(2)无序的:unordered_mapunordered_setunordered_multimapunordered_multiset

3、容器适配器
C++提供了三种容器适配器(container adapter):stack,queue和priority_queue。stack和queue基于deque实现,priority_queue基于vector实现。

适配器包括:

stack(栈) 、queue(队列) 、priority_queue(优先级队列) 。

容器类自动申请和释放内存,因此无需new和delete操作。

2、顺序型容器

2.1 vector容器

1、简介

头文件 #include <vector>

vector是STL提供的动态数组,数组的大小可以动态的变化,类似与一个线性数组,索引效率高,插入,删除的效率很低,需要遍历数据列表。

2、优缺点:

优点:支持随机访问,即 [] 操作和 .at(),查询效率高。
缺点:当向头部或中部,插入或删除元素,插入效率低。

3、插入、删除的时间复杂度:

4、定义与初始化

	vector<int> vec1;    //默认初始化,vec1为空
	vector<int> vec2(vec1);  //使用vec1初始化vec2
	vector<int> vec3(vec1.begin(),vec1.end());//使用vec1初始化vec2
	vector<int> vec4(10);    //10个值为0的元素
	vector<int> vec5(10,4);  //10个值为4的元素
	vector<string> vec6(10,"null");    //10个值为null的元素
	vector<string> vec7(10,"hello");  //10个值为hello的元素

5、常用的操作方法

 	vec1.push_back(100);            //添加元素
    int size = vec1.size();         //元素个数
    bool isEmpty = vec1.empty();    //判断是否为空
    cout<<vec1[0]<<endl;        //取得第一个元素
    vec1.insert(vec1.end(),5,3);    //从vec1.back位置插入5个值为3的元素
    vec1.pop_back();              //删除末尾元素
    vec1.erase(vec1.begin(),vec1.end());//删除之间的元素,其他元素前移
    cout<<(vec1==vec2)?true:false;  //判断是否相等==、!=、>=、<=...
    vector<int>::iterator iter = vec1.begin();    //获取迭代器首地址
    vector<int>::const_iterator c_iter = vec1.begin();   //获取const类型迭代器
    vec1.clear();                 //清空元素

6、遍历方法

    //下标法(vector的特有访问方法,一般容器只能通过迭代器访问)
    int length = vec1.size();
    for(int i=0;i<length;i++) {
       cout<<vec1[i]<<endl;
    }
    //迭代器法
    vector<int>::const_iterator iterator = vec1.begin();
    for(;iterator != vec1.end();iterator++) {
       cout<<*iterator<<endl;
    }

2.2 list容器

1、简介

头文件 #include <list>

由 deque 实现而成,元素也存放在堆中。设计目的是令容器任何位置的添加和删除操作都很快速,作为代价不支持元素的随机访问——为了访问一个元素,只能遍历整个容器。

2、优缺点:

优点:内存不连续,动态操作,可在任意位置插入或删除且效率高。
缺点:不支持随机访问。

3、插入、删除的时间复杂度:

4、定义与初始化

	list<int> lst1;          //创建空list
    list<int> lst2(3);       //创建含有三个元素的list
    list<int> lst3(3,2); //创建含有三个元素的值为2的list
    list<int> lst4(lst2);    //使用lst2初始化lst4
    list<int> lst5(lst2.begin(),lst2.end());  //同lst4

5、常用的操作方法

 	lst1.assign(lst2.begin(),lst2.end());  //分配值
    lst1.push_back(10);                    //添加值
    lst1.pop_back();                   //删除末尾值
    lst1.begin();                      //返回首值的迭代器
    lst1.end();                            //返回尾值的迭代器
    lst1.clear();                      //清空值
    bool isEmpty1 = lst1.empty();          //判断为空
    lst1.erase(lst1.begin(),lst1.end());                        //删除元素
    lst1.front();                      //返回第一个元素的引用
    lst1.back();                       //返回最后一个元素的引用
    lst1.insert(lst1.begin(),3,2);         //从指定位置插入3个值为2的元素
    lst1.rbegin();                         //返回第一个元素的前向指针
    lst1.remove(2);                        //相同的元素全部删除
    lst1.reverse();                        //反转
    lst1.size();                       //含有元素个数
    lst1.sort();                       //排序
    lst1.unique();                         //删除相邻重复元素

6、遍历方法

//迭代器法
    for(list<int>::const_iterator iter = lst1.begin();iter != lst1.end();iter++) {
       cout<<*iter<<endl;
    }

2.3 deque容器

1、简介

头文件 #include <deque>

deque容器类与vector类似,支持随机访问和快速插入删除,它在容器中某一位置上的操作所花费的是线性时间。与vector不同的是,deque还支持从开始端插入数据:push_front()。其余类似vector操作方法的使用。

2、优缺点:

优点:支持随机访问,即 [] 操作和 .at(),查询效率高;当向两端,插入或删除元素,插入效率高。
缺点:当向中部,插入或删除元素,插入效率低。

3、插入、删除的时间复杂度:

4、定义与初始化

deque<int> c   ;  //产生一个空的deque,其中没有任何元素
deque<int> c1(c2); //产生另一个同型deque的副本(所有元素都被拷贝)
deque<int> c(n) ;  //产生一个大小为n的deque
deque<int> c(n , elem) ;  //产生一个大小为n的deque,每个元素值都是elem。
dequer<int> c(begin,end); //产生一个deque,以区间[begin ; end]做为元素初值

5、常用的操作方法

c.size();         //返回当前的元素数量
c.empty();       //判断大小是否为零。等同于c.size() == 0,但可能更快
c.max_size();    //可容纳元素的最大数量
c.at(idx) ;       //返回索引为idx所标示的元素。如果idx越界,抛出out_of_range
c[idx] ;         //返回索引idx所标示的元素。不进行范围检查
c.front() ;       //返回第一个元素,不检查元素是否存在
c.back();        //返回最后一个元素
c.begin();       //返回一个随机迭代器,指向第一个元素
c.end();         //返回一个随机迭代器,指向最后元素的下一位置
c1 = c2  ;        //将c2的所有元素赋值给c1;
c.assign(n , elem);    //将n个elem副本赋值给c
c.assing(beg , end);   //将区间[beg;end]中的元素赋值给c;
c.push_back(elem);   //在尾部添加元素elem
c.pop_back()    ;    //移除最后一个元素(但不回传)
c.push_front()   ;   //在头部添加元素elem
c.pop_front()    ;   //移除头部一个元素(但不回传)
c.erase(pos)    ;   //移除pos位置上的元素,返回一元素位置,如 c.erase( c.begin() + 5)移除第五个元素
c.insert(pos , elem); //在pos位置插入一个元素elem,并返回新元素的位置
c.insert(pos , n , elem); //在pos位置插入n个元素elem,无返回值
c.insert(pos , beg , end);
c.resize(num);       //将容器大小改为num。可更大或更小。
c.resize(num , elem);  //将容器大小改为num,新增元素都为 elem
c.clear();            //移除所有元素,将容器清空

3、有序关联容器

3.1 set(集合)和 multiset(多重集合)

multisetset用法几乎一致,只是multiset可以允许值重复而已,在此就不重复介绍了。

1、简介

头文件 #include <set>

set(集合):由红黑树实现,其中每个元素只包含一个关键字并依据其值自动排序,支持高效的关键字查询操作,每个元素值只能出现一次,不允许重复。插入和删除效率比用其他序列容器高,因为对于关联容器来说,不需要做内存拷贝和内存移动。
multiset(多重集合):唯一的区别是插入的元素可以相同。

2、优缺点:

优点:关键字查询高效,且元素唯一,以及能自动排序。
缺点:每次插入值的时候,都需要调整红黑树,效率有一定影响。

3、插入、删除的时间复杂度:

4、定义与初始化

	set<int> iset(ivec.begin(), ivec.end());	// 根据迭代器范围内的元素初始化
	set<int> set1; 	// 创建一个空的set集合

5、常用的操作方法

	set1.insert("the"); //第一种方法:直接添加
	iset2.insert(ivec.begin(), ivec.end());//第二种方法:通过指针迭代器
    set1.size();    //元素个数
    set1.empty();   //判断空
    set1.find();	// 返回一个迭代器,此迭代器指向集合中其值与指定值相等的元素的位置。
    set1.begin();	// 返回一个迭代器,此迭代器指向集合中的第一个元素。
    set1.end();		// 返回超过末尾迭代器。
	set1.cbegin();	// 返回一个常量迭代器,此迭代器指向集合中的第一个元素。
	set1.cend();	// 返回一个超过末尾常量迭代器。

6、遍历方法

	//正向迭代器
    for(set<int>::iterator iter = set1.begin();iter!=set1.end();iter++) {
       cout << *iter << endl;
    }
    //反向迭代
	for(set<int>::iterator iter = set1.rbegin();iter!=set1.rend();iter++) {
       cout << *iter << endl;
    }

7、删除方法

	set1.erase(iterator it);	//删除迭代器指向的对象
	set1.erase(iterator first,iterator last);	//删除first到last迭代器范围中的内容
	set1.erase(value);	//通过值删除
	set1.clear();	// 清空所有元素,相对于set1.erase(set1.begin(),set1.end());

3.2 map(映射)和multimap(多重映射)

multimapmap用法几乎一致,只是multimap可以允许值重复而已,在此就不重复介绍了。

1、简介

头文件 #include <map>

map(映射):由红黑树实现,其中每个元素都是一些 键值对(key-value):关键字起索引作用,值表示与索引相关联的数据。每个元素有一个键,是排序准则的基础。每一个键只能出现一次,不允许重复。插入和删除效率比用其他序列容器高,因为对于关联容器来说,不需要做内存拷贝和内存移动。

multimap(多重映射):唯一的区别是插入的元素(值)可以相同,即同一个键可以对应多个值。

2、优缺点:

优点:关键字查询高效,且元素唯一,以及能自动排序。把一个值映射成另一个值,可以创建字典。
缺点:每次插入值的时候,都需要调整红黑树,效率有一定影响。

3、插入、删除的时间复杂度:

4、定义与初始化

	map<k, v>m 		// 创建一个名为m的空map对象,其键和值的类型分别为k和v
	map<k, v> m(m2) // 创建m2的副本m,m与m2必须有相同的键类型和值类型
	map<k, v> m(b, e) 	// 创建map类型的对象m,存储迭代器b和e标记的范围内所有元素的副本。元素的类型必须能转换为pair<const k, v>

5、常用的操作方法

	// 插入元素
	map1[3] = "Saniya";                    //添加一个键为 3,值为"Saniya"的键-值对
    map1.insert(map<int,string>::value_type(2,"Diyabi"));//插入元素
    map1.insert(pair<int,string>(1,"Siqinsini")); //插入一个pair类型的键-值对
    map1.insert(make_pair<int,string>(4,"V5")); //通过make_pair插入一个pair类型的键-值对
    map1.insert(iterator first,iterator last);	// 插入另一个map在迭代器范围内的数据
    //其他
	map<int,string>::iterator iter_map = map1.begin();//取得迭代器首地址
    int key = iter_map->first;             //取得key
    string value = iter_map->second;       //取得value
    map1.size();                       //元素个数
    map1.empty();                       //判断空
    map1.find();	// 返回一个迭代器,此迭代器指向映射中其键与指定键相等的元素的位置。
    map1.begin();	// 返回一个迭代器,此迭代器指向映射中的第一个元素。
    map1.end();		// 返回超过末尾迭代器。
	map1.cbegin();	// 返回一个常量迭代器,此迭代器指向映射中的第一个元素。
	map1.cend();	// 返回一个超过末尾常量迭代器。
	map1.count();	// 返回映射中其键与参数中指定的键匹配的元素数量。

6、遍历方法

	//正向迭代器
    for(map<int,string>::iterator iter = map1.begin();iter!=map1.end();iter++) {
       int keyk = iter->first;
       string valuev = iter->second;
    }
    //反向迭代
	for(map<int,string>::iterator iter = map1.rbegin();iter!=map1.rend();iter++) {
       int keyk = iter->first;
       string valuev = iter->second;
    }

7、删除方法

	map1.erase(iterator it);	//删除迭代器指向的对象
	map1.erase(iterator first,iterator last);	//删除first到last迭代器范围中的内容
	map1.erase(const Key&key);	//通过关键字删除
	map1.clear();	// 清空所有元素,相对于map1.erase(map1.begin(),map1.end());

4、无序关联容器

4.1 unordered_map/unordered_multimap

unordered_multimap unordered_map用法几乎一致,只是unordered_multimap 可以允许值重复而已,在此就不重复介绍了。

1、简介

头文件 #include <unordered_map>

unordered_map和map类似,都是存储的key-value的值,可以通过key快速索引到value。不同的是unordered_map不会根据key的大小进行排序,存储时是根据key的hash值判断元素是否相同,即unordered_map内部元素是无序的。unordered_map的底层是一个防冗余的哈希表(开链法避免地址冲突)。

哈希表最大的优点,就是把数据的存储和查找消耗的时间大大降低,时间复杂度为O(1);而代价仅仅是消耗比较多的内存。哈希表的查询时间虽然是O(1),但是并不是unordered_map查询时间一定比map短,因为实际情况中还要考虑到数据量。
2、优缺点:

优点: 因为内部实现了哈希表,因此其查找速度非常的快
缺点: 哈希表的建立比较耗费时间

3、初始化

unordered_map<string, int> map1; map1[string("abc")] = 1; map1["def"] = 2;//创建空map,再赋值
unordered_map<string, int> map2(map1);    //拷贝构造
unordered_map<string, int> map3(map1.find("abc"), map1.end());    //迭代器构造
unordered_map<string, int> map4(move(map2));    //移动构造
unordered_map<string, int> map5 {{"this", 100},{"can", 100},};//使用initializer_list初始化

4、常见用法

map1.at("abc");	//查找具有指定key的元素,返回value
map1.find("abc");    //查找键为key的元素,找到返回迭代器,失败返回end()
map1.count("abc");    //返回指定key出现的次数,0或1
map1.emplace(make_pair("str1", 1));    //使用pair的转换移动构造函数,返回pair<unordered_map<string, int>::iterator, bool>
map1.emplace("str2", 2);    //使用pair的模板构造函数,如果key已经存在则什么都不做
map1.insert(pair<string ,int>("sex", 1));//插入元素,返回pair<unordered_map<string, int>::iterator, bool>
map1.insert(unordered_map<string, int>::value_type("sex", 1));//插入元素,如果key已经存在则什么都不做
map1.insert(make_pair("sex", 1));//插入元素,返回pair<map<string, int>::iterator, bool>,插入成功second为true,失败为flase
map1.insert({"sex", 1});    //使用initializer_list插入元素
map1.insert(map1.end(), {"sex", 1});//指定插入位置,如果位置正确会减少插入时间
map1.insert(map2.begin(), map2.end());//使用范围迭代器插入
map1.erase("abc");	    //删除操作,成功返回1,失败返回0
map1.erase(map1.find("abc"));	    //删除操作,成功返回下一个pair的迭代器
map1.erase(map1.begin(), map1.end());    //删除map1的所有元素,返回指向end的迭代器
map1.empty();        //是否为空
map1.size();        //大小
map1.bucket_count();    //返回容器中的桶数
map1.bucket_size(1);    //返回1号桶中的元素数
map1.bucket("abc");    //abc这个key在哪一个桶
map1.load_factor();    //负载因子,返回每个桶元素的平均数,即size/float(bucket_count);
map1.max_load_factor();//返回最大负载因子
map1.max_load_factor(2);//设置最大负载因子为2,rehash(0)表示强制rehash
map1.rehash(20);//设置桶的数量为20,并且重新rehash
map1.reserve(20);//将容器中的桶数设置为最适合元素个数,如果20大于当前的bucket_count乘max_load_factor,则增加容器的bucket_count并强制重新哈希。如果20小于该值,则该功能可能无效。
unordered_map<string, int>::iterator it = map1.begin();	    //返回指向map1首元素的迭代器
unordered_map<string, int>::const_iterator c_it = map1.cbegin();	    //返回指向map1首元素的常量迭代器
unordered_map<string, int>::local_iterator it = map1.begin(1);//返回1号桶中的首元素迭代器
unordered_map<string, int>::const_local_iterator c_it = map1.cbegin(1);//返回1号桶中的首元素的常量迭代器
pair<unordered_map<string, int>::iterator, unordered_map<string, int>::iterator> it = map1.equal_range("abc");//返回一个pair,pair里面第一个变量是lower_bound返回的迭代器,第二个迭代器是upper_bound返回的迭代器
map1.clear();        //清空

5、示例

4.2 unordered_set/unordered_multiset

unordered_multiset unordered_set 用法几乎一致,只是unordered_multiset 可以允许值重复而已,在此就不重复介绍了。

1、简介

头文件 #include <unordered_set>

unordered_set基于哈希表,数据插入和查找的时间复杂度很低,几乎是常数时间,而代价是消耗比较多的内存,无自动排序功能。底层实现上,使用一个下标范围比较大的数组来存储元素,形成很多的桶,利用hash函数对key进行映射到不同区域进行保存。

2、优缺点:

优点: 因为内部实现了哈希表,因此其查找速度非常的快
缺点: 哈希表的建立比较耗费时间

3、初始化

unordered_set<int> set1; //创建空set
unordered_set<int> set2(set1);    //拷贝构造
unordered_set<int> set3(set1.begin(), set1.end());    //迭代器构造
unordered_set<int> set4(arr,arr+5);    //数组构造
unordered_set<int> set5(move(set2));    //移动构造
unordered_set<int> set6 {1,2,10,10};//使用initializer_list初始化

4、常见用法

set1.find(2);    //查找2,找到返回迭代器,失败返回end()
set1.count(2);    //返回指2出现的次数,0或1
set1.emplace(3);    //使用转换移动构造函数,返回pair<unordered_set<int>::iterator, bool>
set1.insert(3);    //插入元素,返回pair<unordered_set<int>::iterator, bool>
set1.insert({1,2,3});    //使用initializer_list插入元素
set1.insert(set1.end(), 4);//指定插入位置,如果位置正确会减少插入时间,返回指向插入元素的迭代器
set1.insert(set2.begin(), set2.end());//使用范围迭代器插入
set1.erase(1);	    //删除操作,成功返回1,失败返回0
set1.erase(set1.find(1));	    //删除操作,成功返回下一个pair的迭代器
set1.erase(set1.begin(), set1.end());    //删除set1的所有元素,返回指向end的迭代器
set1.empty();        //是否为空
set1.size();        //大小
set1.bucket_count();    //返回容器中的桶数
set1.bucket_size(1);    //返回1号桶中的元素数
set1.bucket(1);    //1在哪一个桶
set1.load_factor();    //负载因子,返回每个桶元素的平均数,即size/float(bucket_count);
set1.max_load_factor();//返回最大负载因子
set1.max_load_factor(2);//设置最大负载因子为2,rehash(0)表示强制rehash
set1.rehash(20);//设置桶的数量为20,并且重新rehash
set1.reserve(20);//将容器中的桶数设置为最适合元素个数,如果20大于当前的bucket_count乘max_load_factor,则增加容器的bucket_count并强制重新哈希。如果20小于该值,则该功能可能无效。
unordered_set<int>::iterator it = set1.begin();	    //返回指向set1首元素的迭代器
unordered_set<int>::const_iterator c_it = set1.cbegin();	    //返回指向set1首元素的常量迭代器
unordered_set<int>::local_iterator it = set1.begin(1);//返回1号桶中的首元素迭代器
unordered_set<int>::const_local_iterator c_it = set1.cbegin(1);//返回1号桶中的首元素的常量迭代器
pair<unordered_set<int>::iterator, unordered_set<int>::iterator> it = set1.equal_range(1);//返回一个pair,pair里面第一个变量是lower_bound返回的迭代器,第二个迭代器是upper_bound返回的迭代器
set1.clear();        //清空

5、容器适配器

5.1 stack容器

1、简介

头文件 #include <stack>

C++ Stack(堆栈) 是一个容器类的改编,为程序员提供了堆栈的全部功能,——也就是说实现了一个先进后出(FILO)的数据结构。

2、插入、删除的时间复杂度:

3、常用的操作方法

	s1.empty() //堆栈为空则返回真
	s1.pop() //移除栈顶元素
	s1.push() //在栈顶增加元素
	s1.size() //返回栈中元素数目
	s1.top() //返回栈顶元素

5.2 queue容器

1、简介

头文件 #include <queue>

queue具有先进先出的数据结构。持新增元素、移除元素、从最底端加入元素、取最顶端元素。

2、插入、删除的时间复杂度:

3、常用的操作方法

	q.push(x); 	//入队,将x元素接到队列的末端;
	q.pop(); 	//出队,弹出队列的第一个元素,并不会返回元素的值;
	q.front();  //访问队首元素
	q.back(); 	//访问队尾元素
	q.size(); 	// 访问队中的元素个数
	q.empty(); 	//判断队列是否为空

5.3 priority_queue容器

1、简介

头文件 #include <queue>

priority_queue实现了优先队列这一ADT,也就是我们常说的堆。但要明晰的是,优先队列是一种ADT,而堆是它的一种具体实现。在默认状态下,priority_queue实现的是大根堆,但你可以通过模板特化从而实现小根堆。

定义:priority_queue<Type, Container, Functional>

Type 就是数据类型,Container 就是容器类型(Container必须是用数组实现的容器,比如vector,deque等等,但不能用 list。STL里面默认用的是vector),Functional 就是比较的方式。

当需要用自定义的数据类型时才需要传入这三个参数,使用基本数据类型时,只需要传入数据类型,默认是大顶堆,如下所示:

 //升序队列,小顶堆
 priority_queue <int,vector<int>,greater<int> > q;
 //降序队列,大顶堆
 priority_queue <int,vector<int>,less<int> >q;
 //greater和less是std实现的两个仿函数(就是使一个类的使用看上去像一个函数。
 //其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了)

2、插入、删除的时间复杂度:

3、常用的操作方法

	q.push(x); 	//入队,将x元素接到队列的末端,(并排序)
	q.pop(); 	//出队,弹出队列的第一个元素,并不会返回元素的值;
	q.front();  //访问队首元素
	q.back(); 	//访问队尾元素
	q.size(); 	// 访问队中的元素个数
	q.empty(); 	//判断队列是否为空

整理博客不易,如果本文对你有所帮助,请给博主 点赞 + 收藏 + 关注!谢谢!

参考:https://blog.csdn.net/u014465639/article/details/70241850
https://blog.csdn.net/like_that/article/details/98446479
https://blog.csdn.net/weixin_43844677/article/details/104902417
https://blog.csdn.net/zhuikefeng/article/details/104738544


转载:https://blog.csdn.net/weixin_43142797/article/details/105851147
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场