# 顺序容器
容器库是
类
与算法
的汇集
# 数组
- 静态的连续数组
array
- 动态连续数组
vector
# array
# 头文件
#include <array> |
# 定义
std::array<int,3> a = {1,2,3}; |
注意:当期长度为零时,array (N==0) 有特殊情况,此时 array.begin ()==array.end (), 并拥有某个唯一值,在零长亦可将 array 上调用 front 或 back () 是未定义的
# 元素访问
at
: 访问指定
的元素,同时进行越界检查operator[]
: 访问指定的元素front
: 访问第一个
元素back
: 访问最后一个
元素data
: 直接访问底层数组的指针,返回的指针使得返回 [data (),data ()+size ()] 始终是合法范围,即使容器为空 (此时 data () 不可解引用)。对于底层元素存储的指针,对于非空容器,返回的指针与首元素地址比较相等
//array 数组元素访问 | |
#include <iostream> | |
#include <array> | |
using namespace std; | |
int main(void) | |
{ | |
array<int,8> a = { 1,2,3,4,5,6,7,8 }; | |
cout << "at(2)=" << a.at(2) << endl; | |
cout << "operator[2]=" << a[2] << endl; | |
cout << "首front=" << a.front() << endl; | |
cout << "尾back=" << a.back() << endl; | |
//a.data 返回 array 首元素地址 | |
cout << "data =" << a.data()[2] << endl; | |
// 遍历数组 | |
for (int i = 0; i < a.size() ; i++) | |
{ | |
cout << a[i] << " "; | |
} | |
cout << endl; | |
return 0; | |
} |
# 迭代器
begin/cbegin
: 访问指向起始的迭代器
end / cend
:返回指向末位的迭代器rbegin / crbegin
:返回指向起始的逆向
迭代器rend / crend
: 返回指向末位的逆向迭代器
//array 的迭代器 | |
#include <iostream> | |
#include <array> | |
using namespace std; | |
int main(void) | |
{ | |
array<int,6> a = { 1,2,3,4,5,6 }; | |
// 打印首元素 | |
//cout << *a.begin() << endl; | |
// 打印所有元素 | |
//for_each(a.cbegin(), a.cend(), | |
// [](int x){ | |
// cout << x << " "; | |
// }); | |
//cout << "\n"; | |
//// 不加 c | |
//for_each(a.begin(), a.end(), | |
// [](int x) | |
// { | |
// cout << x << " "; | |
// }); | |
//cout << "\n"; | |
// 使用 while 正序 | |
auto first = a.cbegin(); | |
auto last = a.cend(); | |
while (first != last) | |
{ | |
cout << *first << " "; | |
(first)++; | |
} | |
cout << endl; | |
// 逆序 r | |
// 使用 while | |
auto first_r = a.rbegin(); | |
auto last_r = a.rend(); | |
while (first_r !=last_r) | |
{ | |
cout << *first_r << " "; | |
first_r++; | |
} | |
cout << endl; | |
return 0; | |
} | |
// 输出 | |
/* | |
1 2 3 4 5 6 | |
6 5 4 3 2 1 | |
*/ |
# 容器
empty
: 检查容器是否为空,若为空 0, 否则 1size
: 返回容纳的元素数max_size
:返回可容纳的最大元素数
#include <iostream> | |
#include <array> | |
using namespace std; | |
int main(void) | |
{ | |
array<int, 3> a = { 1,2,3 }; | |
array <int, 0> no; | |
// 判断是否为空 | |
cout << boolalpha; // 把 bool 值显示为 true 或 false | |
cout << "a.empty() : " << a.empty() << endl; | |
cout << "no.empty() : " << no.empty() << endl; | |
// 返回元素个数 | |
cout << "a.size() :" << a.size() << endl; | |
// 返回元素最大,由于 array 大小固定等于 size | |
cout << "a.max_size() :" << a.max_size() << endl; | |
return 0; | |
} | |
/* | |
a.empty() : false | |
no.empty() : true | |
a.size() :3 | |
a.max_size() :3 | |
*/ |
# 操作
fill
: 以指定值填充容器swap
: 交换内容
#include <iostream> | |
#include <array> | |
using namespace std; | |
int main(void) | |
{ | |
array<int, 3> a; | |
array<int, 3> b ; | |
// 判断是否为空 | |
cout << boolalpha; // 把 bool 值显示为 true 或 false | |
cout << "a.empty() : " << a.empty() << endl; | |
// 将所有元素用 2 填充 | |
a.fill(2); | |
b.fill(6); | |
// 将 a 的元素与 b 元素交换 | |
a.swap(b); | |
// 返回 a 元素个数 | |
cout << "a.size() :" << a.size() << endl; | |
// 返回 a 的所有元素 | |
cout << "A数组:"; | |
auto first_ra = a.rbegin(); | |
auto last_ra = a.rend(); | |
while (first_ra != last_ra) | |
{ | |
cout << *first_ra << " "; | |
first_ra++; | |
} | |
cout << endl; | |
// 返回 b 元素个数 | |
cout << "b.size() :" << a.size() << endl; | |
// 返回 b 的所有元素 | |
auto first_rb = b.rbegin(); | |
auto last_rb = b.rend(); | |
cout << "B数组:"; | |
while (first_rb != last_rb) | |
{ | |
cout << *first_rb << " "; | |
first_rb++; | |
} | |
cout << endl; | |
return 0; | |
} | |
/* | |
a.empty () : false | |
a.size () :3 | |
A 数组:6 6 6 | |
b.size () :3 | |
B 数组:2 2 2 | |
*/ |
# 辅助类
tuple_size(std::array)
: 提供用于编译时常量表达式访问 array 中元素数量的方法tuple_element<std::array>
: 获取 array 元素的类型。提供 tuple 接口,提供 array 元素类型编译时代下标访问
/* 辅助类 */ | |
#include <iostream> | |
#include <array> | |
using namespace std; | |
template<class T> | |
void test(T t) | |
{ | |
int a[tuple_size<T>::value];// 能用于编译时 | |
cout << tuple_size<T>::value << endl; | |
} | |
int main(void) | |
{ | |
array<float, 3> arr = { 1,2,3 }; | |
test( arr ); | |
return 0; | |
} | |
/* | |
3 | |
*/ |
# vector
# 头文件
vector
是封装动态数组的顺序容器,vector
的存储是自动管理的,按需扩张收缩,vector
通产占用多余
静态数组的空间,因此要分配更多内存以管理将来的增长。vector
所用的方式不在每次插入元素时,而只在额外内存耗尽时重分配。分配的内存总量可用capacity()
函数查询,可以通过调用shrink_to_fit()
返回多出的内存给系统。重分配通常是性能上有开销的操作,如果元素数量已知,那么 reserve () 函数可用于消除重分配
#include <vector> |
# 元素访问
at
: 访问指定的元素,同时进行越界检查operator[]
:访问指定的元素front
:访问第一个元素back
:访问之后一个元素data
:直接访问底层数组
# 迭代器
begin/cbegin
:返回指向起始的迭代器end/cend
:返回指向末位的迭代器rbegin/crbegin
: 返回指向起始的逆向迭代器rend/crend
: 返回指向末尾的逆向迭代器
# 容量
empty
: 检查容器是否为空size
: 返回容纳的元素数max_size
: 返回可容纳的最大元素数 (根据系统或库实现限制的容器可保有的元素最大数量),此值通常反应容器大小的理论极限reserve
: 预留存储空间capacity
: 返回当前存储空间能够容纳的元素数shrink_to_fit
:通过释放未使用的内存减少内存的使用
/* | |
* project: vector 容器语法 | |
*/ | |
#include <iostream> | |
#include <vector> | |
using namespace std; | |
int main(void) | |
{ | |
vector<int> a; | |
// 返回当前可容纳元素容量的理论最大值 | |
cout << "Maximum size of a vector is :" << a.max_size() << endl; | |
//capacity 返回容器当前已为之分配空间的元素数 | |
cout << "capacity size of a vector is :" << a.capacity() << endl; | |
// 增加 vector 的容量到大约或等于 new_cap 的值,参数 vector 的新容量 | |
a.reserve(10); | |
//capacity 返回容器当前已为之分配空间的元素数 | |
cout << "New capacity size of a vector is :" << a.capacity() << endl; | |
a = { 1,2,3,4 }; | |
// 请求移除未使用的容量 | |
a.shrink_to_fit(); | |
//capacity 返回容器当前已为之分配空间的元素数 | |
cout << "Shrink capacity size of a vector is :" << a.capacity() << endl; | |
return 0; | |
} | |
/* | |
Maximum size of a vector is :4611686018427387903 | |
capacity size of a vector is :0 | |
New capacity size of a vector is :10 | |
Shrink capacity size of a vector is :4 | |
*/ |
# 修改器
clear
: 清除内容insert
: 插入元素- 参数;
pos
: 将内容那个插入到它前面的迭代器value
: 要插入的元素值first,last
:要插入的元素范围,不能是指向调用 insert 所用的容器中的迭代器ilist
:要插入来源的 initializer_list- 注意:如果新的 size () 大于留的 capacity 就会导致
重分配
,如果新的 size () 大于 capacity (),那么所有迭代器和引用都会失效,否则只有在插入点前的迭代器和引用会保持有效
#include <iostream> | |
//project: | |
#include <vector> | |
#include <algorithm> | |
#include <iterator> | |
using namespace std; | |
// 动态数组输出函数 | |
void print(int id, const vector<int>& a) | |
{ | |
cout << id << ". "; | |
for (const int x : a) | |
{ | |
cout << x << " "; | |
} | |
cout << endl; | |
} | |
int main(void) | |
{ | |
// 创建动态数组 | |
vector<int> c1 (3,100); | |
// 插入元素 | |
print(1, c1); | |
// 在 c1 受元素插入 200 | |
auto it = c1.begin(); | |
it = c1.insert(it, 200); | |
print(2, c1); | |
// 将上面的两个值 it 初始位置插入两个 300 | |
c1.insert(it, 2, 300); | |
print(3, c1); | |
//it 已经是失效,提供新迭代器 | |
it = c1.begin(); | |
// 创建 c2 数组 | |
vector<int>c2(2, 400); | |
// 在 c1 中插入 it 的位置插入 c2 从 begin 到 end | |
c1.insert(next(it, 2), c2.begin(), c2.end()); | |
print(4, c1); | |
return 0; | |
} | |
/* | |
1. 100 100 100 | |
2. 200 100 100 100 | |
3. 300 300 200 100 100 100 | |
4. 300 300 400 400 200 100 100 100 | |
*/ |
emplace
: 原位构造元素,直接与pos
前插入元素到容器中push_back
: 将元素添加到容器末尾
emplace_back
: 在容器末尾就地构造元素
#define _CRT_SECURE_NO_WARNINGS | |
#include <iostream> | |
#include <string.h> | |
#include <vector> | |
class MyString | |
{ | |
public: | |
MyString(const char* str = NULL);// 普通构造函数 | |
MyString(const MyString& other);// 拷贝构造函数 | |
~MyString(void);// 析构函数 | |
MyString& operator = (const MyString& other);// 赋值函数 | |
private: | |
char* m_data;// 用于保存字符串 | |
}; | |
// 普通构造函数 | |
MyString::MyString(const char* str) | |
{ | |
if (str == NULL) | |
{ | |
m_data = new char[1]; | |
*m_data = '\0'; | |
} | |
else | |
{ | |
int length = strlen(str); | |
m_data = new char[length + 1]; | |
strcpy(m_data, str); | |
} | |
std::cout << "construct:" << m_data << std::endl; | |
} | |
// String 的析构函数 | |
MyString::~MyString(void) | |
{ | |
std::cout << "deconstruct:" << m_data << std::endl; | |
delete[] m_data; | |
} | |
// 拷贝构造函数 | |
MyString::MyString(const MyString& other) | |
{ | |
int length = strlen(other.m_data); | |
m_data = new char[length + 1]; | |
strcpy(m_data, other.m_data); | |
std::cout << "copy construct:" << m_data << std::endl; | |
} | |
// 赋值函数 | |
MyString& MyString::operator = (const MyString& other) | |
{ | |
std::cout << "copy assignment" << std::endl; | |
if (this == &other) | |
return *this; | |
if (m_data) | |
delete[] m_data; | |
int length = strlen(other.m_data); | |
m_data = new char[length + 1]; | |
strcpy(m_data, other.m_data); | |
return *this; | |
} | |
int main(void) | |
{ | |
{ | |
std::cout << "对比push_back和emplace_back的效率" << std::endl; | |
std::cout << "push_back"<<std::endl; | |
std::vector<MyString> vStr; | |
// 预先分配,否则整个 vector 在容量不够的情况下重新分配内存 | |
vStr.reserve(100); | |
// 将元素增加到末尾 | |
vStr.push_back(MyString("can ge ge blog")); | |
} | |
{ | |
std::cout << "emplace_back" << std::endl; | |
std::vector<MyString> vStr; | |
// 预先分配,否则整个 vector 在容量不够的情况下重新分配内存 | |
vStr.reserve(100); | |
vStr.emplace_back("hello world"); | |
} | |
return 0; | |
} | |
/* | |
对比 push_back 和 emplace_back 的效率 | |
push_back | |
construct:can ge ge blog | |
copy construct:can ge ge blog | |
deconstruct:can ge ge blog | |
deconstruct:can ge ge blog | |
emplace_back | |
construct:hello world | |
deconstruct:hello world | |
*/ |
从运行结果行,
emplace_back
只调用一次构造和析构,其效率
不言而喻
erase
: 擦除元素- 移除位于
pos
的元素 - 移除范围
[first,last]
中的元素
- 移除位于
pop_back
: 移除莫末元素resize
: 改变容器中可存储元素的个数- 参数:
count
:容量的大小value
:用以初始化新元素的值
#include <iostream> | |
#include <vector> | |
using namespace std; | |
int main(void) | |
{ | |
vector<int> a = { 1,2,3 }; | |
for (const auto& el : a) | |
{ | |
cout << el << " "; | |
} | |
cout << endl; | |
// 增加大小,多余的赋值为 0 | |
a.resize(5); | |
for (const auto& el : a) | |
{ | |
cout << el << " "; | |
} | |
cout << endl; | |
// 添加到 7,赋值为 6 | |
a.resize(7,6); | |
for (const auto& el : a) | |
{ | |
cout << el << " "; | |
} | |
cout << endl; | |
} | |
/* | |
1 2 3 | |
1 2 3 0 0 | |
1 2 3 0 0 6 6 | |
*/ |
swap
: 交换内容
将内容以及容器与
other
交换,不
在单独的元素上调用任何移动,复制或交换操作
# deque
deque
双端队列,允许在它的首尾快速插入语删除,在deque
任一端插入或删除都不会使指向其余元素的指针或引用失效。deque
的存储按需自动扩展及收缩
# 元素访问
at
: 访问指定
的元素,同时进行越界检查operator[]
: 访问指定的元素front
: 访问第一个
元素back
: 访问最后一个
元素
# 迭代器
begin/cbegin
:返回指向起始的迭代器end/cend
:返回指向末位的迭代器rbegin/crbegin
: 返回指向起始的逆向迭代器rend/crend
: 返回指向末尾的逆向迭代器
# 容量
empty
: 检查容器是否为空size
: 返回容纳的元素数max_size
: 返回可容纳的最大元素数 (根据系统或库实现限制的容器可保有的元素最大数量),此值通常反应容器大小的理论极限shrink_to_fit
:通过释放未使用的内存减少内存的使用
# 修改器
clear
: 清楚内容insert
: 插入元素emplace
:原位构造元素erase
: 擦除元素push_back
:将元素添加到容器末尾emplace_back
:将容器末尾就地构造元素pop_back
: 移除末元素pop_front
:移除首元素push_front
:插入元素到容器起始emplace_front
:在容器头部原位构造元素resize
: 改变容器中可存储元素的个数swap
: 交换内容
#include <iostream> | |
#include <deque> | |
using namespace std; | |
int main(void) | |
{ | |
deque<int> a = { 1,2,3,4,5 }; | |
// | |
a.push_back(6); | |
a.push_front(5); | |
// 打印 | |
for (int n : a) | |
{ | |
cout << n << " "; | |
} | |
cout << endl; | |
a.pop_back(); | |
a.pop_front(); | |
for (int n : a) | |
{ | |
cout << n << " "; | |
} | |
cout << endl; | |
return 0; | |
} | |
/* | |
5 1 2 3 4 5 6 | |
1 2 3 4 5 | |
*/ |
# forward_list
forward_list
支持从容器中的任何位置
快速插入和移除元素的容器,不支持快速随机访问。它实现为单链表
,且实质上与其在 C 中实现相比无任何开销,与 list 相比,此容器不需要双向迭代中提供更有效利用空间的存储
# 元素访问
front
: 访问第一个元素
# 迭代器
before_begin\cbefore_begin
: 返回指向第一个元素之前的迭代器begin/cbegin
: 返回指向起始的迭代器end/cend
: 返回指向末位的迭代器
# 容量
- empty: 检查容器是否为空
- max_size: 返回可容纳的最大元素数
# 修改器
clear
: 清楚内容insert_after
: 在某个元素后插入新元素- 参数:
pos
: 内容将其插入到其后的迭代器value
: 要插入的元素值count
: 要插入的副本数first,last
:要插入的元素范围ilist
: 插入值来源 initializer_list
emplace_after
:在元素后原位构造元素erase_after
:擦除元素后的元素- 参数;
pos
: 指向前驱要移除元素的迭代器first,last
要移除的元素范围
push_front
: 插入元素到容器起始emplace_front
:在容器头部构造元素pop_front
: 移除首元素resize
: 改变容器中可存储元素的个数swap
: 交换内容
#include <iostream> | |
#include <forward_list> | |
#include <string> | |
#include <vector> | |
using namespace std; | |
// 重载输出 | |
template<typename T> | |
ostream& operator<<(ostream& s, const forward_list<T>& v) | |
{ | |
s.put('['); | |
char comma[3] = { '\0',' ,','\0' }; | |
for (const auto& e : v) | |
{ | |
s << comma << e; | |
comma[0] = ', '; | |
} | |
return s << ']'; | |
} | |
int main(void) | |
{ | |
forward_list<string> words{ "the","forgurt","is","alse","cursed" }; | |
cout << "words:" << words << endl; | |
auto beginIn = words.begin(); | |
words.insert_after(beginIn, "strawberry"); | |
// insert_after (3) | |
auto anotherIt = beginIn; | |
++anotherIt; | |
anotherIt = words.insert_after(anotherIt, 2, "strawberry"); | |
std::cout << "words: " << words << '\n'; | |
// insert_after (4) | |
std::vector<std::string> V = { "apple", "banana", "cherry" }; | |
anotherIt = words.insert_after(anotherIt, V.begin(), V.end()); | |
std::cout << "words: " << words << '\n'; | |
// insert_after (5) | |
words.insert_after(anotherIt, { "jackfruit", "kiwifruit", "lime", "mango" }); | |
std::cout << "words: " << words << '\n'; | |
// 更改元素个数 | |
words.resize(6); | |
std::cout << "words: " << words << '\n'; | |
// 移除首元素 | |
words.pop_front(); | |
std::cout << "words: " << words << '\n'; | |
// 从容器中移除指定元素 | |
words.erase_after(words.begin()); | |
std::cout << "words: " << words << '\n'; | |
// | |
words.emplace_after(words.begin(), "hello"); | |
std::cout << "words: " << words << '\n'; | |
// 清除元素 | |
words.clear(); | |
return 0; | |
} | |
/* | |
words:[the ,forgurt ,is ,alse ,cursed] | |
words: [the ,strawberry ,strawberry ,strawberry ,forgurt ,is ,alse ,cursed] | |
words: [the ,strawberry ,strawberry ,strawberry ,apple ,banana ,cherry ,forgurt ,is ,alse ,cursed] | |
words: [the ,strawberry ,strawberry ,strawberry ,apple ,banana ,cherry ,jackfruit ,kiwifruit ,lime ,mango ,forgurt ,is ,alse ,cursed] | |
words: [the ,strawberry ,strawberry ,strawberry ,apple ,banana] | |
words: [strawberry ,strawberry ,strawberry ,apple ,banana] | |
words: [strawberry ,strawberry ,apple ,banana] | |
words: [strawberry ,hello ,strawberry ,apple ,banana] | |
*/ |
# 操作
merge
: 合并两个已排序列表,链表以升序排序,不复制元素,并且操作后容器 other 会变为空
,splice_afte
r: 从另一个 forward_list 移动元素- 参数:
pos
:指向将插入内容到其后的元素的迭代器other
: 移动内容来源的另一个容器it
指向从 other 移动到 * this 的元素的迭代器的前驱迭代器first,last
,从 other 移动到 * this 的元素范围
remove/remove_if
: 移除满足特定标准的元素- 参数:
value
- 要移除的元素的值p
若应移除该元素则返回 true 的一元谓词
reverse
: 将该链表的所有元素顺序反转unique
: 删除连续的重复元素sort
: 对元素进行排序
#include <iostream> | |
#include <forward_list> | |
#include <list> | |
using namespace std; | |
ostream& operator<<(ostream& ostr, const forward_list<int>& list) | |
{ | |
for (auto& i : list) | |
{ | |
ostr << " " << i; | |
} | |
return ostr; | |
} | |
int main(void) | |
{ | |
forward_list<int> list1 = { 5,9,1,3,3,3,9}; | |
forward_list<int> list2 = { 8,7,2,3,4,5 }; | |
forward_list<int> list3 = { 66,99,33 }; | |
// 对列表排序 | |
list1.sort(); | |
list2.sort(); | |
cout << "list1" << list1 << endl; | |
cout << "list2" << list2 << endl; | |
list1.merge(list2); // 合并后 list2 为空 | |
cout << "合并后" << list1 << endl; | |
// 插入 | |
list1.splice_after(list1.cbegin(), list3,list3.cbegin(), list3.cend()); | |
cout << "插入后" << list1 << endl; | |
// 移除满足条件的值 | |
list1.remove(1);// 移除等于 1 的 | |
cout << "移除1后" << list1 << endl; | |
// 移除 n 大于 10 的 | |
list1.remove_if([](int n) { return n > 10; }); | |
cout << "移除大于10后" << list1 << endl; | |
// 将所有元素反转 | |
list1.reverse(); | |
cout << "列表反转后:" << list1 << endl; | |
// 删除重复元素 | |
list1.unique(); | |
cout << "去重后:" << list1 << endl; | |
return 0; | |
} | |
/* | |
list1 1 3 3 3 5 9 9 | |
list2 2 3 4 5 7 8 | |
合并后 1 2 3 3 3 3 4 5 5 7 8 9 9 | |
插入后 1 99 33 2 3 3 3 3 4 5 5 7 8 9 9 | |
移除 1 后 99 33 2 3 3 3 3 4 5 5 7 8 9 9 | |
移除大于 10 后 2 3 3 3 3 4 5 5 7 8 9 9 | |
列表反转后: 9 9 8 7 5 5 4 3 3 3 3 2 | |
去重后: 9 8 7 5 4 3 2 | |
*/ |
# list
支持
常量
时间从容器任何位置插入和移除元素的容器。它通常实现为双向链表
# 元素访问
front
: 访问第一个元素back
: 访问最后一个元素
# 迭代器
begin/cbegin
: 返回指向起始的迭代器end/cend
: 返回指向末尾的迭代器rbegin/crbegin
: 返回指向起始的逆向迭代器rend/crend
: 返回指向末尾的逆向迭代器
# 容量
empty
: 检查容器是否为空size
: 返回容纳的元素数max_size
: 返回可容纳的最大元素数
# 修改器
clear
: 清楚内容insert
: 插入元素emplace
:原位构造元素erase
:擦除元素push_back
: 在元素添加到容器末尾emplace_back
:在容器末尾就地构造元素pop_back
;移除末元素push_front
: 插入元素到容器起始emplace_front
:在容器头部构造元素pop_front
: 移除首元素resize
: 改变容器中可存储元素的个数swap
: 交换内容
# 操作
merge
: 合并两个已排序
列表splice
: 从另一个 list 中移动元素remove/remove_if
: 移除满足特定标准的元素reverse
: 将该链表的所有元素顺序反转
unique
: 删除连续的重复
元素sort
: 对元素进行排序
参考资料:
cppreference