为了建立数据结构和算法的一套标准,并且降低他们之间的耦合关系,以提升各自的独立性、弹性、交互操作性(相互合作性,interoperability),诞生了STL。
STL提供了六大组件,彼此之间可以组合套用,这六大组件分别是:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器。
STL六大组件的交互关系,容器通过空间配置器取得数据存储空间,算法通过迭代器存储容器中的内容,仿函数可以协助算法完成不同的策略的变化,适配器可以修饰仿函数。
STL容器就是将运用最广泛的一些数据结构实现出来。
常用的数据结构:数组(array) , 链表(list), tree(树),栈(stack), 队列(queue), 集合(set),映射表(map), 根据数据在容器中的排列特性,这些数据分为序列式容器和关联式容器两种。
序列式容器强调值的排序,序列式容器中的每个元素均有固定的位置,除非用删除或插入的操作改变这个位置。Vector容器、Deque容器、List容器等。
关联式容器是非线性的树结构,更准确的说是二叉树结构。各元素之间没有严格的物理上的顺序关系,也就是说元素在容器中并没有保存元素置入容器时的逻辑顺序。关联式容器另一个显著特点是:在值中选择一个值作为关键字key,这个关键字对值起到索引的作用,方便查找。Set/multiset容器 Map/multimap容器
容器 | 底层数据结构 | 时间复杂度 | 有无序 | 可不可重复 |
---|---|---|---|---|
array | 数组 | 随机读改 O(1) | 无序 | 可重复 |
vector | 数组 | 随机读改、尾部插入、尾部删除 O(1)头部插入、头部删除 O(n) | 无序 | 可重复 |
deque | 双端队列 | 头尾插入、头尾删除 O(1) | 无序 | 可重复 |
forward_list | 单向链表 | 插入、删除 O(1) | 无序 | 可重复 |
list | 双向链表 | 插入、删除 O(1) | 无序 | 可重复 |
stack | deque / list | 顶部插入、顶部删除 O(1) | 无序 | 可重复 |
queue | deque / list | 尾部插入、头部删除 O(1) | 无序 | 可重复 |
priority_queue | vector /max-heap | 插入、删除 O(log2n) | 有序 | 可重复 |
set | 红黑树 | 插入、删除、查找 O(log2n) | 有序 | 不可重复 |
multiset | 红黑树 | 插入、删除、查找 O(log2n) | 有序 | 可重复 |
map | 红黑树 | 插入、删除、查找 O(log2n) | 有序 | 不可重复 |
multimap | 红黑树 | 插入、删除、查找 O(log2n) | 有序 | 可重复 |
unordered_set | 哈希表 | 插入、删除、查找 O(1) 最差 O(n) | 无序 | 不可重复 |
unordered_multiset | 哈希表 | 插入、删除、查找 O(1) 最差 O(n) | 无序 | 可重复 |
unordered_map | 哈希表 | 插入、删除、查找 O(1) 最差 O(n) | 无序 | 不可重复 |
unordered_multimap | 哈希表 | 插入、删除、查找 O(1) 最差 O(n) | 无序 | 可重复 |
array 是固定大小的顺序容器,它们保存了一个以严格的线性顺序排列的特定数量的元素。
方法 | 说明 |
---|---|
begin | 返回指向数组容器中第一个元素的迭代器 |
end | 返回指向数组容器中最后一个元素之后的理论元素的迭代器 |
rbegin | 返回指向数组容器中最后一个元素的反向迭代器 |
rend | 返回一个反向迭代器,指向数组中第一个元素之前的理论元素 |
cbegin | 返回指向数组容器中第一个元素的常量迭代器(const_iterator) |
cend | 返回指向数组容器中最后一个元素之后的理论元素的常量迭代器(const_iterator) |
crbegin | 返回指向数组容器中最后一个元素的常量反向迭代器(const_reverse_iterator) |
crend | 返回指向数组中第一个元素之前的理论元素的常量反向迭代器(const_reverse_iterator) |
size | 返回数组容器中元素的数量 |
max_size | 返回数组容器可容纳的最大元素数 |
empty | 返回一个布尔值,指示数组容器是否为空 |
operator[] | 返回容器中第 n(参数)个位置的元素的引用 |
at | 返回容器中第 n(参数)个位置的元素的引用 |
front | 返回对容器中第一个元素的引用 |
back | 返回对容器中最后一个元素的引用 |
data | 返回指向容器中第一个元素的指针 |
fill | 用 val(参数)填充数组所有元素 |
swap | 通过 x(参数)的内容交换数组的内容 |
get(array | 形如 std::get<0>(myarray);传入一个数组容器,返回指定位置元素的引用 |
relational operators (array) | 形如 arrayA > arrayB;依此比较数组每个元素的大小关系 |
#include<iostream>
#include<array>
using namespace std;
int main()
{
array<int, 8> myArr = {1,3,4,6,9};//固定大小为8
cout << "myArr元素序列:";
for (auto i = 0; i < 8; ++i)
{
cout << myArr[i] << " ";
}
cout << endl;
array<int, 8> myArr1 = {2,3,4,7,8,9};//固定大小为8
cout << "myArr1元素序列:";
for (auto i = 0; i < 8; ++i)
{
cout << myArr1[i] << " ";
}
cout << endl;
myArr.swap(myArr1); //交换两个容器的内容
cout << "交换myArr与myArr1"<< endl;
cout << endl;
cout << "myArr.at(3) = " << myArr.at(3) << endl;//任意访问
cout << "myArr[3] = " << myArr[3] << endl;//任意访问
cout << "myArr.front() = " << myArr.front() << endl;//获取第一个元素
cout << "myArr.back() = " << myArr.back() << endl;//获取最后一个元素
cout << "myArr.data() = " << myArr.data() << endl;//获取第一个元素的指针
cout << "*myArr.data() = " << *myArr.data() << endl;//获取第一个元素的指针指向的元素
cout << "正向迭代器遍历容器:";
for (auto it = myArr.begin(); it != myArr.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
//逆向迭代器测试
cout << "逆向迭代器遍历容器:";
for (auto rit = myArr.rbegin(); rit != myArr.rend(); ++rit)
{
cout << *rit << " ";
}
cout << endl;
//正向常迭代器测试
cout << "正向常迭代器遍历容器:";
for (auto it = myArr.cbegin(); it != myArr.cend(); ++it)
{
cout << *it << " ";
}
cout << endl;
//逆向常迭代器测试
cout << "逆向常迭代器遍历容器:";
for (auto rit = myArr.crbegin(); rit != myArr.crend(); ++rit)
{
cout << *rit << " ";
}
cout << endl;
if(myArr.empty())
cout << "myArr为空 " << endl;
else
cout << "myArr不为空 " << endl;
cout << "myArr.size() = " << myArr.size() << endl;
cout << "myArr.max_size() = " << myArr.max_size() << endl;
return 0;
}
运行结果
vector 是表示可以改变大小的数组的序列容器。
方法 | 说明 |
---|---|
vector | 构造函数 |
~vector | 析构函数,销毁容器对象 |
operator= | 将新内容分配给容器,替换其当前内容,并相应地修改其大小 |
begin | 返回指向容器中第一个元素的迭代器 |
end | 返回指向容器中最后一个元素之后的理论元素的迭代器 |
rbegin | 返回指向容器中最后一个元素的反向迭代器 |
rend | 返回一个反向迭代器,指向中第一个元素之前的理论元素 |
cbegin | 返回指向容器中第一个元素的常量迭代器(const_iterator) |
cend | 返回指向容器中最后一个元素之后的理论元素的常量迭代器(const_iterator) |
crbegin | 返回指向容器中最后一个元素的常量反向迭代器(const_reverse_iterator) |
crend | 返回指向容器中第一个元素之前的理论元素的常量反向迭代器(const_reverse_iterator) |
size | 返回容器中元素的数量 |
max_size | 返回容器可容纳的最大元素数 |
resize | 调整容器的大小,使其包含 n(参数)个元素 |
capacity | 返回当前为 vector 分配的存储空间(容量)的大小 |
empty | 返回 vector 是否为空 |
reserve | 请求 vector 容量至少足以包含 n(参数)个元素 |
shrink_to_fit | 要求容器减小其 capacity(容量)以适应其 size(元素数量) |
operator[] | 返回容器中第 n(参数)个位置的元素的引用 |
at | 返回容器中第 n(参数)个位置的元素的引用 |
front | 返回对容器中第一个元素的引用 |
back | 返回对容器中最后一个元素的引用 |
data | 返回指向容器中第一个元素的指针 |
assign | 将新内容分配给 vector,替换其当前内容,并相应地修改其 size |
push_back | 在容器的最后一个元素之后添加一个新元素 |
pop_back | 删除容器中的最后一个元素,有效地将容器 size 减少一个 |
insert | 通过在指定位置的元素之前插入新元素来扩展该容器,通过插入元素的数量有效地增加容器大小 |
erase | 从 vector 中删除单个元素(position)或一系列元素([first,last)),这有效地减少了被去除的元素的数量,从而破坏了容器的大小 |
swap | 通过 x(参数)的内容交换容器的内容,x 是另一个类型相同、size 可能不同的 vector 对象 |
clear | 从 vector 中删除所有的元素(被销毁),留下 size 为 0 的容器 |
emplace | 通过在 position(参数)位置处插入新元素 args(参数)来扩展容器 |
emplace_back | 在 vector 的末尾插入一个新的元素,紧跟在当前的最后一个元素之后 |
get_allocator | 返回与vector关联的构造器对象的副本 |
swap(vector) | 容器 x(参数)的内容与容器 y(参数)的内容交换。两个容器对象都必须是相同的类型(相同的模板参数),尽管大小可能不同 |
relational operators (vector) | 形如 vectorA > vectorB;依此比较每个元素的大小关系 |
#include <vector>
#include <iostream>
using namespace std;
int main()
{
//构造函数,复制构造函数(元素类型要一致),
vector<int> vecA; //创建一个空的的容器
vector<int> vecB(10,20); //创建一个10个元素,每个元素值为20
vector<int> vecC(vecB.begin(),vecB.end()); //使用迭代器,可以取部分元素创建一个新的容器
vector<int> vecD(vecC); //复制构造函数,创建一个完全一样的容器
//重载=
vector<int> vecE;
vecE = vecB;
//vector::begin(),返回的是迭代器
vector<int> vecF(10); //创建一个有10个元素的容器
cout << "vecF:";
for (int i = 0; i < 10; i++)
{
vecF[i] = i;
cout << vecF[i]<< " ";
}
cout << endl;
//vector::begin() 返回迭代器
vector<int>::iterator Beginit = vecF.begin();
cout<< "vecF.begin():" << *Beginit << endl;
//vector::end() 返回迭代器
vector<int>::iterator EndIter = vecF.end();
EndIter--; //向后移一个位置
cout <<"vecF.end():"<< *EndIter << endl;
//vector::rbegin() 返回倒序的第一个元素,相当于最后一个元素
vector<int>::reverse_iterator ReverBeIter = vecF.rbegin();
cout << "vecF.rbegin(): "<< *ReverBeIter << endl;
//vector::rend() 反序的最后一个元素下一个位置,也相当于正序的第一个元素前一个位置
vector<int>::reverse_iterator ReverEnIter = vecF.rend();
ReverEnIter--;
cout << "vecF.rend():"<< *ReverEnIter << endl;
//vector::size() 返回元素的个数
cout << "vecF.size():"<< vecF.size() << endl;
//vector::max_size()
cout << "vecF.max_size():"<< vecF.max_size() << endl;
//vector::resize()
cout<< "vecF.size():" << vecF.size() << endl;
vecF.resize(5);
cout<< "调整vecF大小后重新赋值:";
for(int k = 0; k < vecF.size(); k++)
cout << vecF[k] << " ";
cout << endl;
//vector::capacity()
cout<< "调整后vecF.size():"<< vecF.size() << endl;
cout<< "调整后vecF.capacity():" << vecF.capacity() << endl;
//vector::empty()
vecB.resize(0);
cout<< "vecB.resize(0)后"<< endl;
cout << "vecB.size():" << vecB.size() << endl;
cout << "vecB.capacity():" << vecB.capacity() << endl;
if(vecB.empty())
cout << "vecB为空"<< endl;
else
cout << "vecB不为空"<< endl;
//vector::reserve() //重新分配存储空间大小
cout<< "vecC.capacity():" << vecC.capacity() << endl; //
vecC.reserve(4);
cout << "vecC.reserve(4)后vecC.capacity(): "<< vecC.capacity() << endl; //10
vecC.reserve(14);
cout << "vecC.reserve(14)后vecC.capacity(): "<< vecC.capacity() << endl; //14
//vector::operator []
cout << "vecF[0]:"<< vecF[0] << endl; //第一个元素是0
//vector::at()
try
{
cout << "vecF.size = " << vecF.size() << endl; //5
cout << vecF.at(6) << endl; //抛出异常
}
catch(out_of_range)
{
cout << "at()访问越界" << endl;
}
//vector::front() 返回第一个元素的值
cout << "vecF.front():"<< vecF.front() << endl; //0
//vector::back()
cout << "vecF.back():"<< vecF.back() << endl; //4
//vector::assign()
cout <<"vecA.size():"<< vecA.size() << endl; //0
vector<int>::iterator First = vecC.begin();
vector<int>::iterator End = vecC.end()-2;
vecA.assign(First,End);
cout << vecA.size() << endl; //8
cout << vecA.capacity() << endl; //8
vecA.assign(5,3); //将丢弃原来的所有元素然后重新赋值
cout << vecA.size() << endl; //5
cout << vecA.capacity() << endl; //8
//vector::push_back()
cout << *(vecF.end()-1) << endl; //4
vecF.push_back(20);
cout << *(vecF.end()-1) << endl; //20
//vector::pop_back()
cout << *(vecF.end()-1) << endl; //20
vecF.pop_back();
cout << *(vecF.end()-1) << endl; //4
//vector::swap()
cout << "vecF:";
for (int i = 0; i < vecF.size(); i++)
{
vecF[i] = i;
cout << vecF[i]<< " ";
}
cout << endl;
cout << "vecD:";
for (int d = 0; d < vecD.size(); d++)
{
vecD[d] = d;
cout << vecD[d]<< " ";
}
cout << endl;
vecF.swap(vecD); //交换这两个容器的内容
cout <<"vecD与vecF交换后:" <<endl;
cout << "vecF:";
for(int f = 0; f < vecF.size(); f++)
cout << vecF[f] << " ";
cout << endl;
cout << "vecD:";
for (int d = 0; d <vecD.size(); d++)
cout << vecD[d] << " ";
cout << endl;
//vector::clear()
vecF.clear();
cout << "vecF.clear()后vecF.size():"<< vecF.size() << endl; //0
cout << "vecF.clear()后vecF.capacity():"<< vecF.capacity() << endl; //10
return 0;
}
运行结果
deque容器为一个给定类型的元素进行线性处理,像向量一样,它能够快速地随机访问任一个元素,并且能够高效地插入和删除容器的尾部元素。但它又与vector不同,deque支持高效插入和删除容器的头部元素,因此也叫做双端队列。
deque的中控器:
deque是由一段一段的定量连续空间构成。一旦有必要在deque的前端或尾端增加新空间,便配置一段定量连续空间,串接在整个deque的头端或尾端。deque的最大任务,便是在这些分段的定量连续空间上,维护其整体连续的假象,并提供随机存取的接口。避开了“重新配置、复制、释放”的轮回,代价则是复杂的迭代器结构。
deque采用一块所谓的map(不是STL的map容器)作为主控。
map是一小块连续空间,其中每个元素(此处称为一个节点,node)都是指针,指向另一段(较大的)连续线性空间,称为缓冲区。
缓冲区才是deque的储存空间主体。
template<class T, class Alloc = alloc, size_t BufSiz = 0>
class deque{
public :
typedef T value_type ;
typedef value_type* pointer ;
...
protected :
//元素的指针的指针(pointer of pointer of T)
// 其实就是T**,一个二级指针,维护一个二维数组
typedef pointer* map_pointer ;
protected :
map_pointer map ; //指向map,map是块连续空间,其内的每个元素
//都是一个指针(称为节点),指向一块缓冲区
size_type map_size ;//map内可容纳多少指针
...
};
map其实是一个T**,也就是说它是一个指针,所指之物也是一个指针,指向型别为T的一块空间。
方法 | 说明 |
---|---|
deque | 构造函数 |
push_back | 在当前的最后一个元素之后 ,在 deque 容器的末尾添加一个新元素 |
push_front | 在 deque 容器的开始位置插入一个新的元素,位于当前的第一个元素之前 |
pop_back | 删除 deque 容器中的最后一个元素,有效地将容器大小减少一个 |
pop_front | 删除 deque 容器中的第一个元素,有效地减小其大小 |
emplace_front | 在 deque 的开头插入一个新的元素,就在其当前的第一个元素之前 |
emplace_back | 在 deque 的末尾插入一个新的元素,紧跟在当前的最后一个元素之后 |
#include "stdafx.h"
#include<iostream>
#include<deque>
using namespace std;
int main()
{
deque<int> d;
d.push_back( 11 );//在 deque 容器的末尾添加一个新元素
d.push_back(20);
d.push_back(35);
cout<<"初始化双端队列d:"<<endl;
for(int i = 0; i < d.size(); i++)
{
cout<<d.at(i)<<"\t";
}
cout<<endl;
d.push_front(10);//容器的开始位置插入一个新的元素,位于当前的第一个元素之前
d.push_front(7);
d.push_front(1);
cout<<"队列d向前陆续插入10、7、1:"<<endl;
for(int i = 0;i < d.size();i++)
{
cout<<d.at(i)<<"\t";
}
cout<<endl;
d.pop_back(); //删除 deque 容器中的最后一个元素,有效地将容器大小减少一个
d.pop_front(); //删除 deque 容器中的第一个元素,有效地减小其大小
cout<<"删除deque最后一个和第一个元素后:"<<endl;
for(int i = 0;i < d.size();i++)
{
cout<<d.at(i)<<"\t";
}
cout<<endl;
return 0;
}
在头文件<forward_list>中,与list类似,区别就是list时双链表,forward_list是单链表,forward_list(单向链表)是序列容器,允许在序列中的任何地方进行恒定的时间插入和擦除操作。在链表的任何位置进行插入/删除操作都非常快。
forward_list的特点:
容器成员函数总结就不写了,太多影响阅读,感兴趣小伙伴戳http://www.cplusplus.com/reference/stl/
list双向链表,是序列容器,允许在序列中的任何地方进行常数时间插入和擦除操作,并在两个方向上进行迭代,可以高效地进行插入删除元素。
使用list容器之前必须加上头文件:#include;
list容器的底层实现:
和 array、vector 这些容器迭代器的实现方式不同,由于 list 容器的元素并不是连续存储的,所以该容器迭代器中,必须包含一个可以指向 list 容器的指针,并且该指针还可以借助重载的 *、++、--、==、!= 等运算符,实现迭代器正确的递增、递减、取值等操作。
template<tyepname T,...>
struct __list_iterator{
__list_node<T>* node;
//...
//重载 == 运算符
bool operator==(const __list_iterator& x){return node == x.node;}
//重载 != 运算符
bool operator!=(const __list_iterator& x){return node != x.node;}
//重载 * 运算符,返回引用类型
T* operator *() const {return *(node).myval;}
//重载前置 ++ 运算符
__list_iterator<T>& operator ++(){
node = (*node).next;
return *this;
}
//重载后置 ++ 运算符
__list_iterator<T>& operator ++(int){
__list_iterator<T> tmp = *this;
++(*this);
return tmp;
}
//重载前置 -- 运算符
__list_iterator<T>& operator--(){
node = (*node).prev;
return *this;
}
//重载后置 -- 运算符
__list_iterator<T> operator--(int){
__list_iterator<T> tmp = *this;
--(*this);
return tmp;
}
//...
}
stack没有迭代器,是一种容器适配器,用于在LIFO(后进先出)的操作,其中元素仅从容器的一端插入和提取。
stack底层一般用list或deque实现,封闭头部即可,不用vector的原因应该是容量大小有限制,扩容耗时
底层用deque实现:
//deque<T> >中间有个空格是为了兼容较老的版本
template <class T, class Sequence = deque<T> >
class stack {
// 以㆘的 __STL_NULL_TMPL_ARGS 会开展为 <>
friend bool operator== __STL_NULL_TMPL_ARGS (const stack&, const stack&);
friend bool operator< __STL_NULL_TMPL_ARGS (const stack&, const stack&);
public:
typedef typename Sequence::value_type value_type;
typedef typename Sequence::size_type size_type;
typedef typename Sequence::reference reference;
typedef typename Sequence::const_reference const_reference;
protected:
Sequence c; // 底层容器
public:
// 以㆘完全利用 Sequence c 的操作,完成 stack 的操作。
bool empty() const { return c.empty(); }
size_type size() const { return c.size(); }
reference top() { return c.back(); }
const_reference top() const { return c.back(); }
// deque 是两头可进出,stack 是末端进,末端出(所以后进者先出)。
void push(const value_type& x) { c.push_back(x); }
void pop() { c.pop_back(); }
};
template <class T, class Sequence>
bool operator==(const stack<T, Sequence>& x, const stack<T, Sequence>& y) {
return x.c == y.c;
}
template <class T, class Sequence>
bool operator<(const stack<T, Sequence>& x, const stack<T, Sequence>& y) {
return x.c < y.c;
}
底层用list实现:
#include<stack>
#include<list>
#include<algorithm>
#include <iostream>
using namespace std;
int main(){
stack<int, list<int>> istack;
istack.push(1);
istack.push(3);
istack.push(5);
cout << istack.size() << endl; //3
cout << istack.top() << endl;//5
istack.pop();
cout << istack.top() << endl;//3
cout << istack.size() << endl;//2
system("pause");
return 0;
}
queue 是一种容器适配器,用于在FIFO(先入先出)的操作,其中元素插入到容器的一端并从另一端提取。
队列不提供迭代器,不实现遍历操作。
template <class T, class Sequence = deque<T> >
class queue {
friend bool operator== __STL_NULL_TMPL_ARGS (const queue& x, const queue& y);
friend bool operator< __STL_NULL_TMPL_ARGS (const queue& x, const queue& y);
public:
typedef typename Sequence::value_type value_type;
typedef typename Sequence::size_type size_type;
typedef typename Sequence::reference reference;
typedef typename Sequence::const_reference const_reference;
protected:
Sequence c;
public:
bool empty() const { return c.empty(); }
size_type size() const { return c.size(); }
reference front() { return c.front(); }
const_reference front() const { return c.front(); }
reference back() { return c.back(); }
const_reference back() const { return c.back(); }
void push(const value_type& x) { c.push_back(x); }
void pop() { c.pop_front(); }
};
template <class T, class Sequence>
bool operator==(const queue<T, Sequence>& x, const queue<T, Sequence>& y) {
return x.c == y.c;
}
template <class T, class Sequence>
bool operator<(const queue<T, Sequence>& x, const queue<T, Sequence>& y) {
return x.c < y.c;
}
优先队列,其底层是用堆来实现的。在优先队列中,队首元素一定是当前队列中优先级最高的那一个。
set 是按照特定顺序存储唯一元素的容器。
template<class _Kty,
class _Pr = less<_Kty>,
class _Alloc = allocator<_Kty> >
class set
multiset允许元素重复而set不允许。
template<class _Kty,
class _Pr = less<_Kty>,
class _Alloc = allocator<_Kty> >
class multiset
map 是关联容器,按照特定顺序存储由 key value (键值) 和 mapped value (映射值) 组合形成的元素。
由于 RB-tree 是一种平衡二叉搜索树,自动排序的效果很不错,所以标准的STL map 即以 RB-tree 为底层机制。又由于 map 所开放的各种操作接口,RB-tree 也都提供了,所以几乎所有的 map 操作行为,都只是转调 RB-tree 的操作行为。
方法 | 说明 |
---|---|
map | 构造函数 |
begin | 返回引用容器中第一个元素的迭代器 |
key_comp | 返回容器用于比较键的比较对象的副本 |
value_comp | 返回可用于比较两个元素的比较对象,以获取第一个元素的键是否在第二个元素之前 |
find | 在容器中搜索具有等于 k的键的元素,如果找到返回一个迭代器,否则返回 map::end |
count | 在容器中搜索具有等于 k(参数)的键的元素,并返回匹配的数量 |
lower_bound | 返回一个非递减序列 [first, last)中的第一个大于等于值 val的位置的迭代器 |
upper_bound | 返回一个非递减序列 [first, last)中第一个大于 val的位置的迭代器 |
equal_range | 获取相同元素的范围,返回包含容器中所有具有与 k等价的键的元素的范围边界 |
multimap 的特性以及用法与 map 完全相同,唯一的差别在于它允许键值重复,因此它的插入操作采用的是底层机制 RB-tree 的 insert_equal() 而非 insert_unique。
unordered_set是基于哈希表,因此要了解unordered_set,就必须了解哈希表的机制。哈希表是根据关键码值而进行直接访问的数据结构,通过相应的哈希函数(也称散列函数)处理关键字得到相应的关键码值,关键码值对应着一个特定位置,用该位置来存取相应的信息,这样就能以较快的速度获取关键字的信息。
template < class Key,
class Hash = hash<Key>,
class Pred = equal_to<Key>,
class Alloc = allocator<Key>
> class unordered_set;
find(beg, end, val); // 返回一个迭代器,指向输入序列中第一个等于 val 的元素,未找到返回 end
find_if(beg, end, unaryPred); // 返回一个迭代器,指向第一个满足 unaryPred 的元素,未找到返回 end
find_if_not(beg, end, unaryPred); // 返回一个迭代器,指向第一个令 unaryPred 为 false 的元素,未找到返回 end
count(beg, end, val); // 返回一个计数器,指出 val 出现了多少次
count_if(beg, end, unaryPred); // 统计有多少个元素满足 unaryPred
all_of(beg, end, unaryPred); // 返回一个 bool 值,判断是否所有元素都满足 unaryPred
any_of(beg, end, unaryPred); // 返回一个 bool 值,判断是否任意(存在)一个元素满足 unaryPred
none_of(beg, end, unaryPred); // 返回一个 bool 值,判断是否所有元素都不满足 unaryPred
adjacent_find(beg, end); // 返回指向第一对相邻重复元素的迭代器,无相邻元素则返回 end
adjacent_find(beg, end, binaryPred); // 返回指向第一对相邻重复元素的迭代器,无相邻元素则返回 end
search_n(beg, end, count, val); // 返回一个迭代器,从此位置开始有 count 个相等元素,不存在则返回 end
search_n(beg, end, count, val, binaryPred); // 返回一个迭代器,从此位置开始有 count 个相等元素,不存在则返回 end
search(beg1, end1, beg2, end2); // 返回第二个输入范围(子序列)在爹一个输入范围中第一次出现的位置,未找到则返回 end1
search(beg1, end1, beg2, end2, binaryPred); // 返回第二个输入范围(子序列)在爹一个输入范围中第一次出现的位置,未找到则返回 end1
find_first_of(beg1, end1, beg2, end2); // 返回一个迭代器,指向第二个输入范围中任意元素在第一个范围中首次出现的位置,未找到则返回end1
find_first_of(beg1, end1, beg2, end2, binaryPred); // 返回一个迭代器,指向第二个输入范围中任意元素在第一个范围中首次出现的位置,未找到则返回end1
find_end(beg1, end1, beg2, end2); // 类似 search,但返回的最后一次出现的位置。如果第二个输入范围为空,或者在第一个输入范围为空,或者在第一个输入范围中未找到它,则返回 end1
find_end(beg1, end1, beg2, end2, binaryPred); // 类似 search,但返回的最后一次出现的位置。如果第二个输入范围为空,或者在第一个输入范围为空,或者在第一个输入范围中未找到它,则返回 end1
for_each(beg, end, unaryOp); // 对输入序列中的每个元素应用可调用对象 unaryOp,unaryOp 的返回值被忽略
mismatch(beg1, end1, beg2); // 比较两个序列中的元素。返回一个迭代器的 pair,表示两个序列中第一个不匹配的元素
mismatch(beg1, end1, beg2, binaryPred); // 比较两个序列中的元素。返回一个迭代器的 pair,表示两个序列中第一个不匹配的元素
equal(beg1, end1, beg2); // 比较每个元素,确定两个序列是否相等。
equal(beg1, end1, beg2, binaryPred); // 比较每个元素,确定两个序列是否相等。
lower_bound(beg, end, val); // 返回一个非递减序列 [beg, end) 中的第一个大于等于值 val 的位置的迭代器,不存在则返回 end
lower_bound(beg, end, val, comp); // 返回一个非递减序列 [beg, end) 中的第一个大于等于值 val 的位置的迭代器,不存在则返回 end
upper_bound(beg, end, val); // 返回一个非递减序列 [beg, end) 中第一个大于 val 的位置的迭代器,不存在则返回 end
upper_bound(beg, end, val, comp); // 返回一个非递减序列 [beg, end) 中第一个大于 val 的位置的迭代器,不存在则返回 end
equal_range(beg, end, val); // 返回一个 pair,其 first 成员是 lower_bound 返回的迭代器,其 second 成员是 upper_bound 返回的迭代器
binary_search(beg, end, val); // 返回一个 bool 值,指出序列中是否包含等于 val 的元素。对于两个值 x 和 y,当 x 不小于 y 且 y 也不小于 x 时,认为它们相等。
fill(beg, end, val); // 将 val 赋予每个元素,返回 void
fill_n(beg, cnt, val); // 将 val 赋予 cnt 个元素,返回指向写入到输出序列最有一个元素之后位置的迭代器
genetate(beg, end, Gen); // 每次调用 Gen() 生成不同的值赋予每个序列,返回 void
genetate_n(beg, cnt, Gen); // 每次调用 Gen() 生成不同的值赋予 cnt 个序列,返回指向写入到输出序列最有一个元素之后位置的迭代器
7.使用输入迭代器的写算法,读取一个输入序列,将值写入到一个输出序列(dest)中
copy(beg, end, dest); // 从输入范围将元素拷贝所有元素到 dest 指定定的目的序列
copy_if(beg, end, dest, unaryPred); // 从输入范围将元素拷贝满足 unaryPred 的元素到 dest 指定定的目的序列
copy_n(beg, n, dest); // 从输入范围将元素拷贝前 n 个元素到 dest 指定定的目的序列
move(beg, end, dest); // 对输入序列中的每个元素调用 std::move,将其移动到迭代器 dest 开始始的序列中
transform(beg, end, dest, unaryOp); // 调用给定操作(一元操作),并将结果写到dest中
transform(beg, end, beg2, dest, binaryOp); // 调用给定操作(二元操作),并将结果写到dest中
replace_copy(beg, end, dest, old_val, new_val); // 将每个元素拷贝到 dest,将等于 old_val 的的元素替换为 new_val
replace_copy_if(beg, end, dest, unaryPred, new_val); // 将每个元素拷贝到 dest,将满足 unaryPred 的的元素替换为 new_val
merge(beg1, end1, beg2, end2, dest); // 两个输入序列必须都是有序的,用小于号运算符将合并后的序列写入到 dest 中
merge(beg1, end1, beg2, end2, dest, comp); // 两个输入序列必须都是有序的,使用给定的比较操作(comp)将合并后的序列写入到 dest 中
8.划分算法,要求双向选代器(bidirectional iterator)
is_partitioned(beg, end, unaryPred); // 如果所有满足谓词 unaryPred 的元素都在不满足 unarypred 的元素之前,则返回 true。若序列为空,也返回 true
partition_copy(beg, end, dest1, dest2, unaryPred); // 将满足 unaryPred 的元素拷贝到到 dest1,并将不满足 unaryPred 的元素拷贝到到 dest2。返回一个迭代器 pair,其 first 成员表示拷贝到 dest1 的的元素的末尾,second 表示拷贝到 dest2 的元素的末尾。
partitioned_point(beg, end, unaryPred); // 输入序列必须是已经用 unaryPred 划分过的。返回满足 unaryPred 的范围的尾后迭代器。如果返回的迭代器不是 end,则它指向的元素及其后的元素必须都不满足 unaryPred
stable_partition(beg, end, unaryPred); // 使用 unaryPred 划分输入序列。满足 unaryPred 的元素放置在序列开始,不满足的元素放在序列尾部。返回一个迭代器,指向最后一个满足 unaryPred 的元素之后的位置如果所有元素都不满足 unaryPred,则返回 beg
partition(beg, end, unaryPred); // 使用 unaryPred 划分输入序列。满足 unaryPred 的元素放置在序列开始,不满足的元素放在序列尾部。返回一个迭代器,指向最后一个满足 unaryPred 的元素之后的位置如果所有元素都不满足 unaryPred,则返回 beg
sort(beg, end); // 排序整个范围
stable_sort(beg, end); // 排序整个范围(稳定排序)
sort(beg, end, comp); // 排序整个范围
stable_sort(beg, end, comp); // 排序整个范围(稳定排序)
is_sorted(beg, end); // 返回一个 bool 值,指出整个输入序列是否有序
is_sorted(beg, end, comp); // 返回一个 bool 值,指出整个输入序列是否有序
is_sorted_until(beg, end); // 在输入序列中査找最长初始有序子序列,并返回子序列的尾后迭代器
is_sorted_until(beg, end, comp); // 在输入序列中査找最长初始有序子序列,并返回子序列的尾后迭代器
partial_sort(beg, mid, end); // 排序 mid-beg 个元素。即,如果 mid-beg 等于 42,则此函数将值最小的 42 个元素有序放在序列前 42 个位置
partial_sort(beg, mid, end, comp); // 排序 mid-beg 个元素。即,如果 mid-beg 等于 42,则此函数将值最小的 42 个元素有序放在序列前 42 个位置
partial_sort_copy(beg, end, destBeg, destEnd); // 排序输入范围中的元素,并将足够多的已排序元素放到 destBeg 和 destEnd 所指示的序列中
partial_sort_copy(beg, end, destBeg, destEnd, comp); // 排序输入范围中的元素,并将足够多的已排序元素放到 destBeg 和 destEnd 所指示的序列中
nth_element(beg, nth, end); // nth 是一个迭代器,指向输入序列中第 n 大的元素。nth 之前的元素都小于等于它,而之后的元素都大于等于它
nth_element(beg, nth, end, comp); // nth 是一个迭代器,指向输入序列中第 n 大的元素。nth 之前的元素都小于等于它,而之后的元素都大于等于它
remove(beg, end, val); // 通过用保留的元素覆盖要删除的元素实现删除 ==val 的元素,返回一个指向最后一个删除元素的尾后位置的迭代器
remove_if(beg, end, unaryPred); // 通过用保留的元素覆盖要删除的元素实现删除满足 unaryPred 的元素,返回一个指向最后一个删除元素的尾后位置的迭代器
remove_copy(beg, end, dest, val); // 通过用保留的元素覆盖要删除的元素实现删除 ==val 的元素,返回一个指向最后一个删除元素的尾后位置的迭代器
remove_copy_if(beg, end, dest, unaryPred); // 通过用保留的元素覆盖要删除的元素实现删除满足 unaryPred 的元素,返回一个指向最后一个删除元素的尾后位置的迭代器
unique(beg, end); // 通过对覆盖相邻的重复元素(用 == 确定是否相同)实现重排序列。返回一个迭代器,指向不重复元素的尾后位置
unique (beg, end, binaryPred); // 通过对覆盖相邻的重复元素(用 binaryPred 确定是否相同)实现重排序列。返回一个迭代器,指向不重复元素的尾后位置
unique_copy(beg, end, dest); // 通过对覆盖相邻的重复元素(用 == 确定是否相同)实现重排序列。返回一个迭代器,指向不重复元素的尾后位置
unique_copy_if(beg, end, dest, binaryPred); // 通过对覆盖相邻的重复元素(用 binaryPred 确定是否相同)实现重排序列。返回一个迭代器,指向不重复元素的尾后位置
rotate(beg, mid, end); // 围绕 mid 指向的元素进行元素转动。元素 mid 成为为首元素,随后是 mid+1 到到 end 之前的元素,再接着是 beg 到 mid 之前的元素。返回一个迭代器,指向原来在 beg 位置的元素
rotate_copy(beg, mid, end, dest); // 围绕 mid 指向的元素进行元素转动。元素 mid 成为为首元素,随后是 mid+1 到到 end 之前的元素,再接着是 beg 到 mid 之前的元素。返回一个迭代器,指向原来在 beg 位置的元素
reverse(beg, end); // 翻转序列中的元素,返回 void
reverse_copy(beg, end, dest);; // 翻转序列中的元素,返回一个迭代器,指向拷贝到目的序列的元素的尾后位置
random_shuffle(beg, end); // 混洗输入序列中的元素,返回 void
random_shuffle(beg, end, rand); // 混洗输入序列中的元素,rand 接受一个正整数的随机对象,返回 void
shuffle(beg, end, Uniform_rand); // 混洗输入序列中的元素,Uniform_rand 必须满足均匀分布随机数生成器的要求,返回 void
min(val1, va12); // 返回 val1 和 val2 中的最小值,两个实参的类型必须完全一致。参数和返回类型都是 const的引引用,意味着对象不会被拷贝。下略
min(val1, val2, comp);
min(init_list);
min(init_list, comp);
max(val1, val2);
max(val1, val2, comp);
max(init_list);
max(init_list, comp);
minmax(val1, val2); // 返回一个 pair,其 first 成员为提供的值中的较小者,second 成员为较大者。下略
minmax(vall, val2, comp);
minmax(init_list);
minmax(init_list, comp);
min_element(beg, end); // 返回指向输入序列中最小元素的迭代器
min_element(beg, end, comp); // 返回指向输入序列中最小元素的迭代器
max_element(beg, end); // 返回指向输入序列中最大元素的迭代器
max_element(beg, end, comp); // 返回指向输入序列中最大元素的迭代器
minmax_element(beg, end); // 返回一个 pair,其中 first 成员为最小元素,second 成员为最大元素
minmax_element(beg, end, comp); // 返回一个 pair,其中 first 成员为最小元素,second 成员为最大元素
lexicographical_compare(beg1, end1, beg2, end2);
lexicographical_compare(beg1, end1, beg2, end2, comp);
需要根据容器的特点和使用场景而定,可能满足需求的不止一种容器。
按是否有序关联性分为:
注意:deque 容器归为使用连续存储空间的这一类,是存在争议的。因为 deque 容器底层采用一段一段的连续空间存储元素,但是各段存储空间之间并不一定是紧挨着的。
选择容器流程图(来源于网络)
选择容器的几点建议:
vector底层是一个动态数组,包含三个迭代器,start和finish之间是已经被使用的空间范围,end_of_storage是整块连续空间包括备用空间的尾部。
当空间不够装下数据(vec.push_back(val))时,会自动申请另一片更大的空间(1.5倍或者2倍),然后把原来的数据拷贝到新的内存空间,接着释放原来的那片空间【vector内存增长机制】。
当释放或者删除(vec.clear())里面的数据时,其存储空间不释放,仅仅是清空了里面的数据。
因此,对vector的任何操作一旦引起了空间的重新配置,指向原vector的所有迭代器会都失效了
2 . vector中的reserve和resize的区别
3 . vector中的size和capacity的区别
4 . vector中erase方法与algorithn中的remove方法区别
5 . vector迭代器失效的情况
it=vec.erase(it)
;。6 . 正确释放vector的内存(clear(), swap(), shrink_to_fit())
vec.clear()
:清空内容,但是不释放内存。vector<int>().swap(vec)
:清空内容,且释放内存,想得到一个全新的vector。vec.shrink_to_fit()
:请求容器降低其capacity和size匹配。vec.clear();vec.shrink_to_fit()
;:清空内容,且释放内存。7 . list的底层原理
8 . 什么情况下用vector,什么情况下用list,什么情况下用deque
9 . priority_queue的底层原理
10 . map 、set、multiset、multimap的底层原理
map 、set、multiset、multimap的底层实现都是红黑树,epoll模型的底层数据结构也是红黑树,linux系统中CFS进程调度算法,也用到红黑树。
红黑树的特性:
11 . 为何map和set的插入删除效率比其他序列容器高
12 . 为何map和set每次Insert之后,以前保存的iterator不会失效?
13 . 当数据元素增多时(从10000到20000),map的set的查找速度会怎样变化?
14 . map 、set、multiset、multimap的特点
15 . 为何map和set的插入删除效率比其他序列容器高,而且每次insert之后,以前保存的iterator不会失效?
16 . 为何map和set不能像vector一样有个reserve函数来预分配数据?
17 . set的底层实现实现为什么不用哈希表而使用红黑树?
18 . hash_map与map的区别?什么时候用hash_map,什么时候用map? 构造函数:hash_map需要hash function和等于函数,而map需要比较函数(大于或小于)。
存储结构:hash_map以hashtable为底层,而map以RB-TREE为底层。
总的说来,hash_map查找速度比map快,而且查找速度基本和数据量大小无关,属于常数级别。而map的查找速度是logn级别。但不一定常数就比log小,而且hash_map还有hash function耗时。
如果考虑效率,特别当元素达到一定数量级时,用hash_map。
考虑内存,或者元素数量较少时,用map。
19 . 迭代器失效的问题
插入操作:
删除操作:
20 . 线程不安全的情况
http://www.cplusplus.com/reference/stl/ https://blog.csdn.net/qq_23350817/article/details/87930715 https://blog.csdn.net/bizhu12/article/details/6769976 http://c.biancheng.net/view/7385.html https://blog.csdn.net/daaikuaichuan/article/details/80717222
本文由哈喽比特于2年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/JWAestcpQTlp2fb1Q45AQg
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。