容器
顺序容器 | 数据结构 |
vector<T> | 可变大小向量 |
list<T> | 双向链表 |
forward_list<T> | 单向链表 |
deque<T> | 双端队列 |
forward_list 是一种专为空链表和极短链表优化过的数据结构,一个空forward_list只占用一个内存字。不提供insert()、erase()、emplace(),而提供 *_after 系列操作;不提供back()、push_back()、pop_back()、emplace_back();不提供reverse_iterator、const_reverse_iterator、rbegin()、crbegin()、crend()、size()
有序关联容器 | 数据结构 |
map<K,V> | 关联数组 |
multimap<K,V> | 允许重复关键字的map |
set<T> | 集合 |
multiset<T> | 允许重复值的集合 |
multi*系列关联容器或集合不提供 [] 和 at()
无序关联容器 | 数据结构 |
unordered_map<K,V> | 采用哈希搜索的map |
unordered_multimap<K,V> | 采用哈希搜索的multimap |
unordered_set<T> | 采用哈希搜索的set |
unordered_multiset<T> | 采用哈希搜搜的multiset |
unordered_* 系列关联容器不提供 <、<=、>、>=
容器适配器 | 数据结构 |
priority_queue<T,C,Cmp> | T 的优先队列;Cmp是优先级函数类型,默认 std::less<T> |
queue<T,C> | T 的队列;支持 push() 和 pop() 操作;容器类型C默认std::deque<T> |
stack<T,C> | T的栈;支持 push() 和 pop() 操作;容器类型C默认std::deque<T> |
T:value_type
一、vector
1.一个vector就是一个给定类型元素的序列,元素在内存中是连续存储的,进行元素的删除插入操作时其他元素可能会移动
2.用一组值来初始化vector(值的类型必须与vector元素类型符合)
//Entry代表“电话簿”结构体,包含string型名字,int型号码
vector<Entry> phone_book = {
{"David Hume",123456},
{"Karl Poper",234567},
{"Bertrand Arthur William Russell",345678}
};
可以通过下标运算符访问元素,如phone_book[0]
可以返回元素的数目,如phone_book.size()
遍历vector元素,for(const auto& x : phone_book)
定义vector时可设置初始大小:
vector<int> v1 = {1, 2, 3,4}; //size为4
vector<string> v2; //size为0
vector<Shape> v3(23); //显示给出size为23;元素初值是nulptr
vector<double> v4(32, 9.9); //显示size为32,元素初值是9.9(32个元素全部初始为9.9)
vector的初始大小随着程序的执行可以被改变
在赋值和初始化时,vector可以被拷贝,如vector<Entry> book2 = phone_book;
拷贝和移动vector是通过构造函数和赋值运算符实现的,vector的赋值过程包括拷贝其中的元素
4.方法1:vector.push_back(),它向vector末尾追加一个新元素
5.元素
vector<T>中,元素T可为:内置数据类型(char、int、double)、用户自定义类型(string、Entry、list<int>、Matrix<double,2>)、指针类型
6.范围检查
标准库vector不进行范围检查(可能越界),可以自定义范围检查
二、list
1.list是一个双向链表
如果希望在一个序列中添加和删除元素的同时无须移动其他元素,则应使用list,电话簿插入删除频繁可使用list
//Entry代表“电话簿”结构体,包含string型名字name,int型号码number
list<Entry> phone_book = {
{"David Hume",123456},
{"Karl Poper",234567},
{"Bertrand Arthur William Russell",345678}
};
2.一般不用下标操作来访问链表元素
3.遍历链表list,for(const auto& x : phone_book):
int get_number(const string& s){
for(const auto& x : phone_book)
if(x.name == s)
return x.number;
return 0;
}
每个标准库容器都提供begin()和end()函数,显示地使用迭代器遍历list(p为指针)
int get_number(const string& s){
for(auto p=phone_book.begin();p!=phone_book.end();++p)
if(p->name == s)
return p->number;
return 0;
}
4.方法1,添加元素:
phone_book.insert(p,ee); //将ee添加到p指向的元素之前
5.方法2,删除元素:
phone_book.erase(q); //删除q指向的元素
6.当只需要一个元素序列,数据量较小时,应该使用vector(vector无论遍历如find()和count()、排序sort()、搜索binary_search()都优于list)
三、map
1.map是一个搜索树(红黑树),也称为关联数组或字典,通常用平衡二叉树实现
//map是值对的容器,(key,value)
map<string,int> phone_book {
{"David Hume",123456},
{"Karl Poper",234567},
{"Bertrand Arthur William Russell",345678}
};
2.map支持下标操作(本质上是一次搜索),下标值应为map的第一个类型key,得到与关键字关联的值value,若key值不存在则向map插入一个新元素,复杂度O(log(n))
int get_number(const string& s){
return phone_book[s];
}
四、unordered_map 无序容器
1.初始化
unordered_map<string,int> phone_book {
{"David Hume",123456},
{"Karl Poper",234567},
{"Bertrand Arthur William Russell",345678}
};
2.下标操作
int get_number(const string& s){
return phone_book[s];
}
五、set 集合:每个元素只出现一次,且自动由小到大排列
set<string> dict;
五、容器的大小和容量
大小是指容器中的元素数目;容器是指在重新分配更多内存之前容器能够保存的元素数目
方法 | 含义 |
x = c.size() | x 是 c 的元素数目 |
c.empty() | c为空吗 |
x = c.max_size() | x 是 c 的最大可能元素数目 |
x = c.capacity() | x 是为 c 分配的空间大小;只适用于 vector 和 string |
c.reserve(n) | 为 c 预留 n 个元素的空间;只适用于 vector 和 string |
c.resize(n) | 将 c 的大小改变为 n;将增加的元素初始化为默认元素值;只适用于顺序容器和string |
c.resize(n,v) | 将 c 的大小改变为 n;将增加的元素初始化为 v;只适用于顺序容器和string |
c.shrink_to_fit() | 令 c.capacity() 等于 c.size();只适用于 vector、deque、string |
c.clear() | 删除 c 的所有元素 |
在改变大小或容量时,元素可能会被移动到新的存储位置。这意味着指向元素的迭代器、指针、引用,可能会失效(即,指向旧元素的位置)
六、栈操作
标准 vector、deque、list(不包括 forward_list 和关联容器)提供了高效的元素序列尾部操作:
栈操作 | 含义 |
c.push_back(x) | 将 x 添加到 c 的尾元素之后(使用拷贝或移动) |
c.pop_back() | 删除 c 的尾元素 |
c.emplace_back(args) | 用 args 构造一个对象,将它添加到 c 的尾元素之后 |