容器
分类
- 序列容器:对象有序排列,用数值进行索引
- 关联容器:对象顺序不重要,用键来进行索引
- 适配器:调整原有容器的行为,使其对外展现新的类型接口或返回新元素
- 生成器:构造元素序列
序列容器
序列容器模板
- array:元素个数固定的序列容器
- vector:元素连续存储的序列容器
- forward_list/list:基于链表/双向链表的容器
- deque:vector与list的折中
- basic_string:提供了对于字符串专门的支持
array
定义
#include <array>
std::array<int,3> a{1,2,3};
- 具有固定长度的容器,内部维护了一个内建数组,与内建数组相比提供了复制操作等接口
- 提供接口:
- 构造:与数组类似
- 成员类型:value_type
- 元素访问:
- []:a[100]超出索引不报错
- at:a.at(100)超出索引报错
- front:返回第一个元素的值
- back:返回最后一个元素的值
- data:返回指向数组第一个元素的指针
- 容量相关:
- empty:是否空
- size:尺寸
- max_size:最大尺寸
- 填充与交换:
- fill:a.fill(100)
- swap:a.swap(b) //成本极高
vector
定义
#include <vector>
vector<int> a{1,2,3};
内部结构
size_t size 大小 |
---|
size_t cap 容量 |
T* buffer |
动态数组实现:将内容存入到一个数组中,若备用空间不足,会开辟更大的空间,将旧的拷贝进去,再释放旧空间,继续插入元素。
接口
- 容量相关
- size:大小
- max_size:最大元素个数
- reserve:预留空间 a.reserve(80) 提高效率
- capacity:容量大小
- shrink_to_fit:将容量缩减至于与当前大小一样(size=capacity)
- 插入
- insert:a.insert(pos, 100) a.insert(pos, 3, 100) 性能很低
- emplace
- 附加元素
- push_back:先构造再复制移动至结尾
- emplace_back:直接在结尾处构造,效率更高
- 元素删除
- pop_back:删除末尾元素
- erase:a.erase(it1, it2)
- clear:删除所有内容
list
定义
#include <list>
std::list<int> list = { 7, 5, 16, 8 };
内部结构
指向上一元素的指针 | 当前元素 | 指向下一元素的指针 |
---|
特性
- 插入,删除成本低
- 随机访问成本高,需要从第一个元素开始向下寻找
- 不支持[]来访问
- pop_front接口:删除首元素
- push_front:在首元素前插入
- splice接口:list1.splice(list1.begin(), list2.begin(), list2.end());将一个list内容插入到另一个list中
forward_list
成本较低的list,只保留指向下一元素的指针
- 只支持递增操作,无rbegin和rend
- 不支持size(为了降低成本)
- 不支持pop_back/push_back(为了降低成本)
- 支持:
- insert_after
- erase_after
- emplace_after
- push_front
- emplace_front
- pop_front
- resize:a.resize(5, val) list小于5补0,大于5删除
- splice_after
deque
双向容器,deque 容器存储数据的空间是由一段一段等长的连续空间构成,各段空间之间并不一定是连续的,可以位于在内存的不同区域。deque的数组中存放这指向各段空间的指针。
#include <deque>
std::deque<int> a{1, 2, 3, 4};
- 双向容器,支持pop_front push_front push_back pop_back
*在元素两端增加或删除元素的效率高,但在序列中间插入或删除元素的效率低
basic_string
字符串相关接口:
- to_string
//支持输入形参int/double/float/long...
int a = 3;
std::string b = to_string(a);
- stoi stol stoll stof stod
std::string a = "123.5";
double b = stod(a);
std::string c = "123";
int d = stoi(c);
- getline
//一次读入一行
#include <iostream>
#include <stream>
int main()
{
std::string name;
std::cout << "What is your name? ";
std::getline(std::cin, name);
std::cout << "Hello " << name << ", nice to meet you.\n";
}
关联容器
关联容器模板
- map 底层红黑树实现
- set 底层红黑树实现
- multiset 底层红黑树实现
- multimap 底层红黑树实现
- unordered_xxx 底层hash表实现
map
定义
#include <map>
int main()
{
std::map<char, int> m{{'a',3}, {'b',4}};
std::cout << m['a'] <<std::endl;
std::map<std::string, int> n { {"CPU", 10}, {"GPU", 15}, {"RAM", 20}, };
for (const auto& [key, value] : n) {
std::cout << key << " = " << value << "; ";
}
}
- 每个元素都为一个键值对 key->value
- 接口:
- count:m.count(‘a’) 不是1就是0
- find:m.find(‘a’) 返回指向特定键的迭代器,若找不到为end()
- begin()
- end()
- erase:erase(key) erase(iter)
- clear
- insert
- merge
- extract 将键值对抽取出来
map<int, string> m{{1, "mango"}, {2, "papaya"}, {3, "guava"}}; auto nh = m.extract(2); nh.key() = 4; m.insert(move(nh));
set
定义
#include <set>
std::set<int> s{100, 3, 56, 7};
for(auto ptr = s.begin(); s != s.end(); s++)
{
std::cout << *ptr <<std::endl;
}
特性
- 顺序不重要
- 元素不能重复,多个元素只会保留一个
- 默认中序遍历,先左子树,再树根,再右子树,输出从小到大排序
- 底层为红黑树实现,因此集合内的元素必须可以比较
构造set对象
//set构造模板
template <class key, class Compare = std::less<key>, class Allocator = std::allocator<key>>
class set;
//compare比较函数,默认为less
std::set<int> a{5, 2, 7}; //a:2 5 7
std::set<int, std::greater<int>> a{5, 2, 7}; //a:7 5 2
//构造函数
set( std::initializer_list<value_type> init,
const Compare& comp = Compare(),
const Allocator& alloc = Allocator() );
set( InputIt first, InputIt last,
const Compare& comp = Compare(),
const Allocator& alloc = Allocator() );
自定义比较函数
struct str
{
int x;
};
bool mycomp(const str& val1, const str& val2)
{
return val1.x < val2.x;
}
int main()
{
std::set<str, decltype(&mycomp)> s({str{1},str{2}}, mycomp);
)
接口函数
- insert s.insert(str{100});
- emplace s.emplace(100); //用100构造这个对象
- erase s.erase(str{100}); 或s.earese(iter);
- find auto a = s.find(key) 找不到返回end()(vector中无find)
- set迭代器返回的是const迭代器,无法通过迭代器修改元素
- extract修改元素,erase+insert的代价很高,先用extract抽取节点
auto x = a.extract(1);
x.value() = 4;
a.insert(std::move(x));
map
树的每一个节点是一个std::pair
定义
#include <map>
std::map<int, bool> m{{1,true}, {2,true}, {3,flase}};
for(auto ptr = m.begin(); ptr != m.end(); ++ptr)
{
std::cout << ptr->first << ptr->second << std::endl;
}
//key必须支持比较大小
接口
- m.insert(std::pair<int, bool>(3, true));
- m.erase(3) 直接删key就行,删pair也行
- m.find(3) 给key返回迭代器
- m.contain(3) 返回bool
- m[100] 若没有键则插入一个pair(key, T())缺省构造
- m.at(100) 没有键就报错
multiset/multimap
在原先基础上允许重复键
定义
#include <set>
#include <map>
std::multiset<int> s{1,3,1};
std::multimap<int,int> s{{1,3},{1,5}};
接口
- find 返回首个找到的元素
- count s.count(2)返回键为2元素的个数
- auto b = s.lower_bound(1) 返回指向首个键不小于1的元素的迭代器
- auto c = s.upper_bound(1) 返回指向首个键大于1的元素的迭代器
- auto d = s.equal_range(1) 返回由上述两个迭代器组成的pair类型对象
unordered_xxx
底层hash表实现,键通过hash函数映射获取索引(表中位置),与set/map相比寻找性能更好。键需要支持转换为hash值和判等两种操作,有时插入很慢(当表大小需要重新扩充时)。
无序容器在存储上组织为一组桶,每个桶为一个链表保存零或多个元素(键值对),无序容器通过一个hash函数将元素映射到桶。无序容器的性能依赖于hash函数的质量和桶的数量和大小
例如10个bucket,键是15,15%10=5,将这个键值对存储到5号bucket
定义
#include <unordered_set>
#include <unordered_map>
std::unordered_set<int> s{3, 1, 5, 4, 1};
std::unordered_map<char,int> m{{'a', 1}, {'b', 2}};
接口
- begin
- end
- empty
- size
- max_size
- clear
- emplace
- insert
- erase
- swap
- extract
- merge
- count
- find
- equal_range
- == != 可以判断 但效率很低
构造自定义hash函数的unordered_xxx
struct str
{
int x;
}
size_t MyHash(const str& val)
{
return val.x;
}
bool MyEqual(const str& val1, const str& val2)
{
return val1.x == val2.x;
}
std::unordered_set<str, decltype(&MyHash), decltype(&MyEqual)> s(4, MyHash, MyEqual);
实际用与自定义类型时可以
struct str
{
int x;
bool operator ==(const str& t) const
{
return (this->x == t.x);
}
};
class MyHash
{
public:
size_t operator ()(const str& t) const
{
return t.x;
}
};
//相当于MyHash f; f(str{1}); 来获取hash值
unordered_set<str, MyHash> a; //可缺省构造了
a.insert(str{});