📂C/C++
🔖C++
2022-02-28 15:08阅读: 104评论: 0推荐: 0

STL Containers用法与示例

Containers

从实现的角度看,容器是一种class template

容器的结构与分类

  1. Sequence containers(序列容器):array、vector、deque(队列)、list(双向链表)、forward_list(单向链表)。
  2. Associative containers(关联容器):set/multiset、map/multimap。
    • set/map的value/key不能重复,一般底层是红黑树实现(一种自平衡二叉查找树),红黑树演示地址
  3. Unordered associative containers(无序关联容器):unordered_set/unordered_multiset,unordered_map/unordered_multimap。

详细资料:
https://en.cppreference.com/w/cpp/container
http://www.cplusplus.com/reference/stl/

  • container adapter(容器适配器):queuestack,虽然看似容器,但其实只是一种容器配接器,因为它们的底部完全借助deque,所有操作都由底层的deque供应。

1 Sequence containers 序列式容器

1.1.array 固定数组

是c++语言本身提供的固定大小的数组

  • 使用场景:类似vector,比数组更安全(不担心越界),但是内容在栈上,且属于定长容器。
  • 支持快速随机访问,不能添加或删除元素。
  • 构造函数、at()、[]、front()、back()、data()、max_size()、swap()、fill()用法如下。
查看代码
copy
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
#include<iostream> #include <array> int main() { using namespace std; array<int, 3> a1{1,2,3}; array<int, 3> a2={3,4,5}; array<string, 2> a3{string("hello"),"word"}; // 成员函数at()会检查范围 操作符[]不会 cout<<a1[1]<<endl; try { cout<<a1.at(5)<<endl; } catch(const std::out_of_range& e) { std::cout << e.what() << '\n'; } //front() back() 取数组第一个和最后一个元素 cout<<a2.front()<<endl; cout<<a2.back()<<endl; //data() 返回数据的指针 cout<<"head addr: "<<a1.begin()<<" --> "<<a1.data()<<endl; //与开头地址相等 cout<<*(a1.data())<<endl; //max_size() 最大成员数 在固定数组中等于size cout<<"max_size:"<<a2.max_size()<<endl; //swap() 交换两个容器 a1.swap(a2); //基于范围的for 循环 for(const auto& s: a3) cout << s << ' '; cout<<endl; //用给定值填充容器 a3.fill("hihi"); for(const auto& s: a3) cout << s << ' '; cout<<endl; return 0; }

1.2. vector 动态数组

可变大小数组,每次满了之后会自动扩容

  • 使用场景:需要快速查找,不需要频繁插入/删除。
  • 用法指南
  • 采用线性连续空间。
  • 支持快速随机访问,在尾部之外的位置插入或删除元素可能很慢。所以提供的是Random Access Iterators。
  • vector是动态空间,一旦旧有空间满了,当新增一个元素,vector会扩大一倍(不同的编译器实现的扩容方式不一致)。
  • push_back() 与emplace_back()区别 参考1 参考2
    • 如果参数是左值,两个调用的都是 copy constructor。
    • 如果参数是右值,两个调用的都是 move constructor(C++ 11后push_back也支持右值)
    • emplace_back支持in-place construction(原地构造,也就是传入普通构造函数的参数即可),直接在容器尾部构造这个元素T,省去了拷贝或移动元素的过程。
  • 当对vector的任何操作引起空间重新配置,指向原vector的所有迭代器就都失效了。
  • 避免使用vector<bool>,它不是一个STL容器。其次,它并不存储bool,它存储的是bool的紧凑表示。可用deque<bool>替代。
  • 容器中数据的布局是否与C兼容,如果是的话,选择vector。
  • 常用构造函数、assign()、capacity()、reserve(size_type)、shrink_to_fit()、clear()、insert()、emplace_back()、push_back()、pop_back()等示例如下。
查看代码
copy
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
#include<iostream> #include <memory> //Allocator #include<vector> int main() { using namespace std; //常用构造函数 //1,vector( size_type count ); 2,vector( std::initializer_list<T> init, const Allocator& alloc = Allocator() ); //3,vector( size_type count,const T& value,const Allocator& alloc = Allocator()); //4,template< class InputIt >vector( InputIt first, InputIt last,const Allocator& alloc = Allocator() ); vector<int> v1(3); //默认值为0 vector<string,allocator<string> > v2({"hello","word","!"}); //类型中可设置空间分配器 vector<int> v3(10,1,allocator<int>()); //10个1值 参数中也可用一个临时对象设置容器空间配置器 vector<int> v4(v1.begin(),v1.end()); vector<vector<int>> v5(10,vector<int>()); //二维数组 10*X vector<int> v6{1,4,1,2,3,1,3,1}; // assign() 替换容器的内容。 作用同 operator= v1.assign(v3.begin(),v3.begin()+4); //用v3范围内的内容替换v1 v3.assign({1,2,3,4,4,56}); //初始化列表化赋值 v4.assign(10,0); //10个0值 for(int x:v4)cout<<x<<" "; cout<<endl; //at()、[]、front()、back()、data()、max_size()、swap() 方法同其他容器一致 //capacity() 当前分配了多少空间 cout<<"maxsize:"<<v4.max_size()<<" size:"<<v4.size()<<" capacity():"<<v4.capacity()<<endl; //reserve(size_type)指定大小内存空间 小于size则忽略 v4.reserve(15); cout<<"capacity():"<<v4.capacity()<<endl; //shrink_to_fit() 释放没用的内存 v4.shrink_to_fit(); cout<<"capacity():"<<v4.capacity()<<endl; //clear() 清空元素 v4.clear(); //1,iterator insert( iterator pos, const T& value ); 在pos前插入元素 //2,void insert( iterator pos, size_type count, const T& value ); pos前插入cout个value //3,void insert( iterator pos, InputIt first, InputIt last );pos前插入[first, last)范围内值 //4,iterator insert( const_iterator pos, std::initializer_list<T> ilist ); pos插入列表值 v4.insert(v4.end(),1); //1 v4.insert(v4.begin(),3,2); // --2 2 2-- 1 v4.insert(v4.begin()+2,v1.begin(),v1.begin()+2); // 2 2 --1 1-- 2 1 v4.insert(v4.begin()+3,{1,2,3,4}); //2 2 1 1 2 3 4 1 2 1 cout<<"v4: "; for(int x:v4)cout<<x<<" "; cout<<endl; //emplace(pos) 是将参数传递给构造函树,在pos位置前,在容器管理的内存空间中直接构造元素。 // 若该位置有其他元素,则先在其他构造,再添加到指定位置前。其它是复制过去的,不是直接构造 v4.emplace(v4.begin(),0); v4.emplace(v4.end(),9); v4.emplace_back(10); //末尾添加 cout<<"v4: "; for(int x:v4)cout<<x<<" "; cout<<endl; //push_back(value) //pop_back() v1.push_back(22); v1.pop_back(); //erase() 删除指定位置和范围[first, last)的元素 v1.erase(v1.begin()); v4.erase(v4.begin(),v4.begin()+4); return 0; }

1.3. list 双向链表

  • 使用场景:需要频繁插入/删除,不需要快速查找。
  • 非连续空间存储,插入删除为常数时间。
  • 插入和合并操作不会使原来的迭代器失效,这点与vector不同。
  • SGI list不仅是一个双向链表,还是环状双向链表。链表尾端有一个空白节点即可实现前开后闭的区间要求。
  • 迭代器不能像vector一样以普通指针作为迭代器。迭代器也不能加常数。
  • 常用函数示例如下
查看代码
copy
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
#include<iostream> #include<list> #include<string> #include<functional> //greater<>() int main() { using namespace std; list<string> l1; list<string> l2(5,string("hello")); //5个hello字符串 list<string> l3(l2.begin(),l2.end()); list<string> l4(l2); list<string> l5({"hello","world"}); list<string> l6{"a","b","c","d","e","f"}; //resize(n) 只保留容器前n个元素 l2.resize(2); //a.merger(b) 合并链表a+=b; 链表第一个元素比较默认小的先放前面,剩下的不变 //被合并链表b变空 l6.merge(l5); l2={"z","d","bb","shuu"};//重新赋值 l3.assign({"a","a","s"}); l2.merge(l3,greater<string>()); //splice 移动元素,拼接在指定位置前 l6.splice(l6.begin(),l2); //l2变为空 l3.assign({"a","a","s"}); list<string>::iterator it=l3.begin(); advance(it,2); //移动迭代器 l3.splice(it,l6); l6.splice(l6.begin(),l3,--it,l3.end()); //pop_front 删除头节点 //pop_back //push_back、emplace_back 队尾新增节点 //push_front 插入头节点 //reverse 反转链表 l6.reverse(); //remove(T) 删除等于T的节点 remove_if(UnaryPredicate P) P为真则删除 l6.remove("s"); //unique 删除 连续 位置上元素的重复值 l3.sort(); //排序 l3.unique(); for(string s:l3) cout<<s<<" "; cout<<endl; return 0; }

1.4. forward_list 单向链表

  • 使用场景:需要list的优势,但只要向前迭代。

1.5 deque 双向队列

  • 使用场景:头尾增删元素很快,随机访问比vector慢一点,因为内部处理堆跳转。中间插入和删除效率较高。
  • 支持快速随机访问,在头尾位置插入或删除速度很慢。

stack

2 associative containers 关联式容器

set/multiset、map/multimap容器底层均基于RB_tree(红黑树)完成。

2.1 RB_tree

  • 点击查看RB_tree的实现
  • 不仅是一个二叉搜索树,而且必须满足以下规则
    • 每个节点不是红色就是黑色。
    • 根节点为黑色。
    • 如果为红,其子节点必须为黑。
    • 任意节点致NULL(树尾端)的任何路径,所含黑色节点必须相同。
  • RB_tree出现原因,AVL树条件太苛刻,新增节点是旋转太多了,耗费性能。
    • RB_tree也是平衡树,不过是黑色平衡。

2.2 set

所有元素会根据元素的键值自动排序

  • 使用场景:需要元素有序,查找/删除/插入性能一样。红黑树效率都是O(logN)。即使是几个亿的内容,最多也查几十次。
  • 与map不同,set的键值(key)就是实值(value)。
  • set不允许两个元素有相同的键值。
  • 与list相同,新增或者删除时,之前的迭代器依然有效。
查看代码
copy
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
#include <set> #include <iostream> using namespace std; int main() { typedef set<int>::iterator itr; set<int> s1{1, 2, 9, 4, 6, 8}; itr it = s1.begin(); advance(it,2); //移动迭代器 //emplace_hint 在可能的位置插入 s1.emplace_hint(it, 0); s1.insert(9); // equal_range 返回一个pair,第一个指向首个不小于 key 的元素,第二个指向首个大于 key 的元素。 pair<itr,itr> range=s1.equal_range(4); cout<<*(range.first)<<endl; cout<<*(range.second)<<endl; //返回第一个指向首个不小于 key 的元素迭代器 itr it2=s1.lower_bound(4); cout<<*it2<<endl; // 返回首个大于 key 的元素迭代器 it2=s1.upper_bound(4); cout<<*it2<<endl; //返回比较值的函数对象 set<int>::value_compare cmp1= s1.value_comp(); //返回比较key的函数对象 set<int>::key_compare cmp1= s1.key_comp(); for (int x : s1) { cout << x << " "; } cout<<endl; return 0; }

部分参考:
https://blog.csdn.net/fatterrier/article/details/115413194

本文作者:oniisan

本文链接:https://www.cnblogs.com/oniisan/p/STLContainers.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Oniisan_Rui  阅读(104)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起