顺序容器
顺序容器的特点是其内部分配地址是连续的,可以轻易通过下标访问。
vector 变长数组
构造函数
直接赋值
1
2
3//vector( std::initializer_list<T> init)
std::vector<int> v{1,2,3};
std::vector<int> v={1,2,3};空矢量
1
2std::vector<int> v;
std::vector<int> v{};赋值为重复数值
1
2
3//vector( size_type count,const T& value)
std::vector<int> v(10,1);//赋10个1
std::vector<int> v(10);//默认赋值为0连续地址赋值
1
2
3
4//template< class InputIt >
//vector( InputIt first, InputIt last)
int base[5]={1,2,3,4,5};
std::vector<int> v(base,base+5);已有矢量初始化
1
2
3//vector( const vector& other );
std::vector<int> base(10,1);
std::vector<int> v(base);其他(如插入、复制)
会在具体函数处说明
访问
下标直接访问(随机访问)
1
cout<<v[i]<<endl;
内置
iterator
迭代器访问
数据入口函数
函数 | 作用 |
---|---|
front() |
返回首元素 |
back() |
返回尾元素 |
示例
1 | std::vector<char> letters {'a', 'b', 'c', 'd', 'e', 'f'}; |
迭代器
函数 | 作用 |
---|---|
begin()/cbegin() |
返回指向首元素的迭代器 |
end()/cend() |
返回指向尾元素之后的迭代器 |
rbegin()/crbegin() |
返回指向尾元素的迭代器 |
rend()/crend() |
返回指向首元素之前的迭代器 |
示例
1 | std::vector<double> d={ 1.9, 2.9, 3.9, 4.9, 5.9 }; |
tips:cbegin
与begin
在stl
容器中,许多返回迭代器的函数都有一个带c
前缀的函数,比如begin
/cbegin
,它们的区别仅仅在于,cbegin
返回的是常量迭代器,其内容不可更改;而begin
返回的迭代器所指的值可以直接修改。
常用函数
at
原型
1 | constexpr reference at( size_type pos ); |
作用
修改pos
处的值
示例
1 | v.at(0)=10; |
assign
原型
1 | //1 |
作用
初始化(数据类型不能变)
示例
1 | vector<int> v; |
data
原型
1 | constexpr T* data() noexcept; |
作用
返回容器首元素地址
示例
1 | std::vector<double> d={ 1.9, 2.9, 3.9, 4.9, 5.9 }; |
empty
原型
1 | constexpr bool empty() const noexcept; |
作用
判断数组是否为空
示例
1 | std::vector<double> d={ 1.9, 2.9, 3.9, 4.9, 5.9 }; |
size
/max_size
原型
1 | constexpr size_type size() const noexcept; |
作用
返回数组大小
返回数组最大限制(要用long long
储存)
示例
1 | std::vector<int> v={1,2,3,4,5,6}; |
reserve
原型
1 | constexpr void reserve( size_type new_cap ); |
作用
为数组申请(保留)存储空间,如果new_cap
小于当前存储空间,则函数不进行任何操作。
如果可以预先估计数组所需存储,利用该函数可以减少动态申请次数以节约时间。
示例
1 | std::vector<int> v={1,2,3,4,5,6}; |
capacity
原型
1 | constexpr size_type capacity() const noexcept; |
作用
返回数组当前申请(保留)的空间大小
注意区分size
和capacity
示例
1 | std::vector<int> v={1,2,3,4,5,6}; |
shrink_to_fit
原型
1 | constexpr void shrink_to_fit(); |
作用
将数组动态申请的空间大小删减为当前存储数据的大小,即释放多余空间,可在数组数据存储完之后节约空间。
示例
注意与上例对比输出结果
1 | std::vector<int> v={1,2,3,4,5,6}; |
clear
原型
1 | constexpr void clear() noexcept; |
作用
删除所有元素但保留申请的空间
示例
1 | std::vector<int> v={1,2,3,4,5,6}; |
insert
原型
1 | constexpr iterator insert( const_iterator pos, const T& value ); |
作用
在pos
前插入数值
注意一次插入后pos
的值会改变,如果第二次还想继续插入,需要重新获取pos
的值。
示例
1 | std::vector<int> v={1,2,3}; |
emplace
原型
1 | template< class... Args > |
作用
在pos
前插入一个元素
示例
1 | std::vector<int> v={1,2,3}; |
tips: insert和emplace的区别
1 |
|
从上面的例子中可以看出,调用insert
时,新元素会依次调用其构造函数和拷贝构造函数,也就是说,insert
的内部原理是先生成一个数据类型的临时结构体,再将该结构体拷贝进形参后进行插入;而emplace
只执行了构造函数,之后直接进行插入。这个区别来源于C++11的可变参数模板,比较复杂。
总而言之,emplace
及其类似函数在插入时时间开销较少,可以优先考虑,但如果希望一次性插入多个元素,则只能使用insert
及其类似函数。
push_back
/emplace_back
原型
1 | constexpr void push_back( const T& value ); |
作用
在末尾插入一个元素
示例
1 | //_int64_结构体见上文 |
erase
原型
1 | constexpr iterator erase( const_iterator pos ); |
作用
删除pos
处元素或删除first
至end
的所有元素(左闭右开)
示例
1 | vector<int> v{1,2,3,4,5,6,7,8,9}; |
pop_back
原型
1 | constexpr void pop_back(); |
作用
从末尾弹出一个元素(没有返回值)
示例
1 | vector<int> v{1,2,3,4,5,6,7,8,9}; |
resize
原型
1 | constexpr void resize( size_type count ); |
作用
修改容量至count
如果当前容量大于count
,则向后删除多余元素
如果当前容量小于count
,则增加的容量填充value
(没有该参数时填充0
)
如果当前容量等于count
,则不进行任何操作
示例
1 | vector<int> v{1,2,3,4,5,6,7,8,9}; |
swap
原型
1 | constexpr void swap( vector& other ) noexcept |
作用
交换两个数值元素,容量可以不同。
交换后各自begin()
迭代器不变。
交换双方数据类型必须一致。
示例
1 | vector<int> v1{1,2,3,4,5}; |
queue 队列
priority_queue优先队列
deque 双向队列
构造函数
直接赋值
1
2
3//deque( std::initializer_list<T> init, const Allocator& alloc = Allocator() );
deque<int> d={1,2,3,4};
deque<int> d{1,2,3,4};用另一个队列构造
1
2
3//deque( const deque& other, const Allocator& alloc );
deque<int> d1{1,2,3};
deque<int> d2(d1);连续空间赋值
1
2
3
4//template< class InputIt >
//deque( InputIt first, InputIt last, const Allocator& alloc = Allocator() );
deque<int> d1{1,2,3};
deque<int> d2(d1.begin(),d1.end());容量和重复赋值
1
2
3//deque( size_type count, const T& value, const Allocator& alloc = Allocator());
deque<int> d1(10,2);
deque<int> d2(10);//默认赋值为0
访问
常用函数
STL容器在很多方面具有相似性,因此这里只给出deque
独特的成员函数
push_front
/pop_front
原型
1 | void push_front( T&& value ); |
作用
在队列开头加入/删除元素(pop
函数没有返回值)
示例
1 | deque<int> d{1,2,3,4,5}; |
emplace_front
/emplace_back
原型
1 | template< class... Args > |
作用
在队列前(末)端插入一个元素
示例
略
相较于vector
所没有的成员函数
data
capacity
reserve
forward_list单向链表
构造函数
访问
不支持随机访问,只能通过迭代器访问。
数据入口函数
函数 | 作用 |
---|---|
front() |
返回首元素 |
迭代器函数
函数 | 作用 |
---|---|
begin()/cbegin() |
返回指向首元素的迭代器 |
end()/cend() |
返回指向尾元素之后的迭代器 |
before_begin()/cbefore_begin() |
返回指向首元素之前的迭代器(head) |
示例
1 | forward_list<int> l{1,2,3,4,5}; |
tips:为什么list
/forward_list
的迭代器不支持加减运算
这个问题要从list
数据结构的角度思考,与vector
不同,list
是由链表构成的,其储存单元的内存并不是连续的。
因此想要访问list
的某一个单元,就必须从头开始进行一个O(n)
的查找,也即不支持随机访问。
list
的每个迭代器相当于一个指向链表节点的”指针“,节点中只保存了下一个节点的相关信息(next
),因此迭代器只支持自增运算,而不能进行自减--
、加+
、减-
运算。
同理,vector
等连续储存的容器则支持加减运算,其本质是在当前地址的基础上加上存储单元大小的若干倍。
常用函数
assign
empty
max_size
clear
insert_after
在指定位置之后插入值
1 | iterator insert_after( const_iterator pos, T&& value ); |
1 | forward_list<int> l{1,2,3,4,5}; |
erase_after
删除pos
之后的一个元素或是删除从first
到end
的所有元素(不包括边界)
1 | iterator erase_after( const_iterator pos ); |
1 | forward_list<int> l1{1,2,3,4,5}; |
“emplace
“
emplace_after
在
pos
之后的位置插入一个元素1
2template< class... Args >
iterator emplace_after( const_iterator pos, Args&&... args );1
2forward_list<int> l1{1,2,3,4,5};
l1.emplace_after(l1.before_begin(),0);
push_front
/pop_front
resize
swap
merge
合并两个已排序序列。
完成后,other
序列被清空。
对于没有比较运算的数据结构,需要自定义comp
函数,函数结构如下:
1 | bool cmp(const Type1& a, const Type2& b); |
要求当第一个参数小于第二个时,返回true
。
1 | void merge( forward_list&& other ); |
1 | //_int64_前面有 |
splice_after
将other
中部分元素转移到当前序列pos
之后的位置
第一种重载模式表示将other
序列全部转移
第二种重载模式表示将other
中it
之后的一个元素转移
第三种重载模式表示将other
中first
到end
中的元素转移(不包括边界)
1 | void splice_after( const_iterator pos, forward_list&& other ); |
1 | forward_list<int> l1(10); |
remove
&remove_if
删除等于value
或使得p
返回true
的元素
1 | void remove( const T& value ); |
1 | bool d2(int a){ |
reverse
反转序列
1 | void reverse() noexcept; |
1 | forward_list<Type> l; |
unique
删除连续出现的重复元素,只保留每个重复序列的第一个元素。
其内部默认使用==
运算判断相同元素,对于不能直接判断的数据结构,要么重载其==
运算;要么传入比较函数p
,p
的规范如下
1 | bool p(const Type1 &a, const Type2 &b); |
当且仅当a
与b
相等时返回true
。
1 | size_type unique(); |
1 | forward_list<Type> l{1,1,2,2,3,3,4,1,1,5,5,6}; |
sort
从小到大排序。
对于不能直接进行<
运算的数据结构,需要重载运算符或者传入函数comp
1 | bool cmp(const Type1& a, const Type2& b); |
当且仅当a<b
时,函数返回true
。
1 | void sort(); |
1 | bool mycmp(int a,int b){ |
list 双向链表
list
和forward_list
的很相似,因此这里只给出它们的区别,不再一一展示list
的成员函数。
数据入口
list
是双向的,因此它在forward_list
的front()
的基础上多了back()
接口。迭代器
list
没有before_begin()
这一迭代器。相应的,
forward_list
中所有xxxx_after
函数也都退化成了xxxx
。插入位置
除了上面所说的
list
中插入位置都在pos
之后(即函数不带after
后缀)以外,由于容器的开口是双向的,list
多了emplace_back
、push_back
、poo_back
这三个函数以对应front
函数。
array 定长数组
构造函数
与其他容器初始化稍有不同,array
只有两种初始化方式。你需要在声明模板的同时声明大小,比如
1 | array<int 6> arr={1,2,3}; |
另外,初始化参数数目可以小于声明的大小,这时多余的空间会用0
填充。
访问
- 随机访问
- 迭代器访问
数据入口函数
函数 | 作用 |
---|---|
front() |
返回首元素 |
back() |
返回尾元素 |
迭代器
函数 | 作用 |
---|---|
begin()/cbegin() |
返回指向首元素的迭代器 |
end()/cend() |
返回指向尾元素之后的迭代器 |
rbegin()/crbegin() |
返回指向尾元素的迭代器 |
rend()/crend() |
返回指向首元素之前的迭代器 |
常用函数
at
data
empty
size
max_size
fill
用value
替换数组内所有元素
1 | void fill( const T& value ); |
1 | array<int,5> a={1,2,3}; |
swap
关联容器
关联容器的内部结构往往是树,因此其单元地址并不是连续的,不支持下标访问;同时,这类容器支持在插入时自动排序。
set 集合
set
是一类有序集合,其内部结构是二叉排序树。
构造函数
1 | std::set<Key,Compare,Allocator> |
set
模板如上,一般只用到Key
和Compare
两项,即数据类型和比较函数
如果数据结构重载了比较运算符,那么Compare
也可以省略。需要注意的是,重载运算符的函数必须是常函数
set
有没有赋重复值的构造函数,这是因为set
本身不允许出现重复元素。
除此之外,set
的构造函数都可以参考其他容器
访问
由于二叉树也是节点结构,因而set
没有随机访问,只能迭代器访问。
迭代器
同array
集合的迭代器只有++
、--
两种运算。
常用函数
empty
size
max_size
clear
insert
1 | std::pair<iterator, bool> insert( value_type&& value ); |
集合的插入函数有所不同。
插入元素不需要指定位置(可以指定但是集合的有序组合,并不影响最终插入的位置,可能影响执行速度)
当不指定插入位置且插入单个元素时返回pair
,它有first
与second
两个成员。前者是插入元素的迭代器;后者是bool
类型,表示插入成功与否。
其他情况与其他迭代器基本一致。
1 | int main(){ |
emplace
emplace
无需指定插入位置,返回值是pair
。
emplace_hint
1 | template <class... Args> |
集合容器用此函数来指定位置插入元素,插入位置是hint
之前(最后集合内顺序还是排序后的)
函数返回最终新元素位置的迭代器;如果插入失败,则返回已存在元素位置的迭代器。
erase
swap
count
1 | size_type count( const Key& key ) const; |
该函数返回集合中key
出现的次数(其实就是在不在集合中)
find
1 | const_iterator find( const Key& key ) const; |
该函数返回key
所在位置的迭代器
1 | int main(){ |
equal_range
1 | std::pair<const_iterator,const_iterator> equal_range( const Key& key ) const; |
返回一个包含两个迭代器的pair
,前者是值为key
的迭代器的下界,后者是上界。
注意区间是左闭右开,即前者是第一个不小于key
的元素的迭代器,后者是第一个大于key
元素的迭代器。
1 | int main(){ |
lower_bound
1 | const_iterator lower_bound( const Key& key ) const; |
返回第一个不小于key
的元素的迭代器。
upper_bound
1 | const_iterator upper_bound( const Key& key ) const; |
返回第一个大于key
元素的迭代器。
key_comp
1 | key_compare key_comp() const; |
返回当前集合的比较函数,即初始化时的Compare
1 | struct cmp{ |
map 字典
构造函数
1 | std::map<Key,T,Compare,Allocator> |
字典的模板如上,在使用时我们需要指定键(key
)、值(T
)的数据类型以及比较函数Compare
1 | template< class InputIt > |
1 | struct cmp{ |
访问
迭代器访问
通过
key
访问字典重载了
[]
运算符,可以通过关键词直接访问。通过该运算符读时,对于存在于字典的
key
,函数返回其值value
;否则返回0
通过该运算符写时,对于存在于字典的
key
,将修改器值;否则插入其中。1
T& operator[]( Key&& key );
1
2
3
4
5
6
7
8
9
10int main(){
map<string,int> m{
{"c",1},
{"b",2},
{"d",3}
};
m["a"]=4;
m["b"]=114;
for(auto p=m.begin();p!=m.end();p++) cout<<p->first<<" : "<<p->second<<endl;
}
迭代器
与set
相同
常用函数
at
用法同[]
empty
size
max_size
clear
insert
和集合的插入类似
1 | template< class P > |
1 | int main(){ |
emplace
/emplace_hint
参考insert
以及集合的同名函数
erase
swap
count
find
equal_range
lower_bound
upper_bound
key_comp
value_comp
类似key_comp
,返回一个比较函数,该函数有两个pair
型参数,函数返回key_comp(pair1.first,pair2.first)
的值。
1 | struct comp{ |
multiset
顾名思义,multiset
是set
的类似物,它允许其中出现重复的元素。它的操作几乎和原型相同。不过在这里,set
中看似无用的equal_range
、lower_bound
、upper_bound
等函数就有了独特的用处。
multimap
它是和multiset
类似的map
变形。