C++ - 标准模板库

[算法(algorithms)、容器(containers)、函数(functions)、迭代器(iterators)] - STL

使用using namespace std;声明命名空间;

也可手动注明std命名空间,std::; 

标准库指的是C++标准委员会定义的一整套标准化的类、函数和对象,旨在提供一致性和可移植性。

C++标准库通常分为两个主要部分:标准库和STL。

注:C++ 标准库则是在 C 标准库的基础上进行了扩展和改进,包含了 C 标准库的所有内容,引入了命名空间和模板等特性,提供了更丰富功能。

一、容器(containers)

1、array(数组)

头文件

#include <array>

定义

array<int,5> myarray = {1,2,3,4,5};

array<int,5> otherarray = myarray;

初始化

std::array<double, 10> values {};

std::array<double, 10> values {0.5,1.0,1.5,,2.0};

访问

可通过下标运算符[]对元素进行操作,还可以通过at/front/back进行操作

for (int i = 0; i < 5; i++) {

    cout << setw(10) << << myarray.at(i) << endl;

}

遍历

可以通过正向和反向迭代器对元素进行遍历

for (auto it = myarray.begin(); it != myarray.end();++it) {

    cout << *it << endl;

}

方法函数

begin()

返回指向容器中第一个元素的随机访问迭代器

end()

返回指向容器最后一个元素之后一个位置的随机访问迭代器,通常和 begin() 结合使用

rbegin()

返回指向最后一个元素的随机访问迭代器

rend()

返回指向第一个元素之前一个位置的随机访问迭代器

crbegin()

rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素

crend()

和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素

size()

返回容器中当前元素的数量,其值始终等于初始化 array类的第二个模板参数 N

max_size()

返回容器可容纳元素的最大数量,其值始终等于初始化 array类的第二个模板参数 N

empty()

判断容器是否为空,和通过 size()==0 的判断条件功能相同,但其效率可能更快

at(n)

返回容器中n 位置处元素的引用,该函数自动检查 n 是否在有效的范围内,如果不是则抛出out_of_range 异常

front()

返回容器中第一个元素的直接引用,该函数不适用于空的 array 容器

back()

返回容器中最后一个元素的直接应用,该函数同样不适用于空的 array 容器

data()

返回一个指向容器首个元素的指针。利用该指针,可实现复制容器中所有元素等类似功能

fill(val)

将val 这个值赋值给容器中的每个元素

array1.swap(array2)

交换 array1 和 array2 容器中的所有元素,但前提是它们具有相同的长度和类型

2、vector(动态数组)

vector为可变长数组(动态数组),定义的vector数组可以随时添加数值和删除元素;

头文件

#include <vector>

定义

// 方式一:一维可变长数组

vector<int>num;    // 定义一个名为num的存int数据的一维数组

vector<double>num; // 定义一个名为num的存double数据的一维数组

vector<node>num;   // node是结构体类型

// 方式二:二维可变长数组

vector<int>num[5];  // 定义可变长二维数组

// 注意:行是不可变的(只有5行),而列可变可以在指定行添加元素

// 第一维固定长度为5,第二维长度可以改变

// 定义一个行和列均可变的二维数组

vector<vectot<int>>num;

初始化

// 1.附加长度

vector<int >v(n);    // 定义一个长度为n的数组,动态定义

// 2.附加长度和初始值

vector<int>v(n,0);  // 所有的元素均为0

访问

// 方式一:单个访问,假设num数组中已经有了5个元素

cout<<num[4]<<"\n";  // 输出第五个数据

// 一二维可变数组和普通数组的访问方法一样

// 方式二:遍历

for(int i = 0;i < num.size();i++)

 cout<<num[i]<<" ";   // 下标范围在[0,num.size()),前开后闭

// 方式三:智能指针

for(auto i : num)

 cout<<i<<" ";

方法函数

相关方法函数如下:c指定为数组名称

c.begin()

返回首元素的迭代器(通俗来说就是地址)

c.end()

返回最后一个元素后一个位置的迭代器(地址)

c.front()

返回第一个数据

c.back()

返回最后一个数据

c.push_back(element)

在尾部加一个数据 O(1)

c.pop_back()

删除最后一个数据 O(1)

emplace()

在指定的位置直接生成一个元素

emplace_back()

在序列尾部生成一个元素,通过完美转发传递给元素构造函数的参数来直接构造元素,不需要创建临时对象,因此可以避免一次拷贝或移动操作,通常在性能上优于 push_back()

c.empty()

判断是否为空,为空返回真,反之返回假

c.size()

返回实际数据个数(unsigned类型) O(1)

c.capacity ()

返回容器存储数据的个数

c.reserve (n)

设置capacity大小,减少内存浪费;

reserve函数能够节省空间,且能够减少内存开辟次数,但是需要提前知道开辟空间大小;

c.resize(n,v)

改变数组大小为n,n个空间数值赋为v,如果没有默认赋值为0

c.erase(first,last)

删除[first,last)的所有元素

删除指定索引元素:

vec.erase(vec.begin() + index);

c.clear()

清除元素个数 O(N),N为元素个数;

size将变成0,但是容量capacity并未发生改变,clear只是删除数据,并未释放vector的内存

如果想要清空vector的元素,使用clear,如果想要释放vector的容量,可以使用swap:

vector<A>().swap(vec);

vec.swap(vector<A>());

c.insert(it,x)

向任意迭代器it插入一个元素x O(N),

例:c.insert(c.begin()+2,-1) 将-1插入c[2]的位置

array1.swap(array2)

交换 array1 和 array2 容器中的所有元素,但前提是它们具有相同的长度和类型

3、stack()

栈为数据结构的一种,是STL中实现的一个先进后出,后进先出的容器;

就像火车进入没有出口的隧道一样,隧道是stack栈容器,火车车厢是入栈元素,火车头先进去,火车尾最后进隧道,当火车倒出来时,火车尾最先出来,火车头最后出来,所有的元素满足先进后出的规则;

注意:栈只能对栈顶元素进行操作,如果想要进行遍历,只能将栈中元素一个个取出来存在数组中;

头文件

#include <stack>

定义

stack<int>sta;

stack<string>sta;

stack<node>sta; // node是结构体类型

方法函数

push()

压栈,增加元素 O(1)

pop()

移除栈顶元素 O(1)

top()

取得栈顶元素(但不删除)O(1)

empty

检测栈内是否为空,空为真 O(1)

size()

返回stack内元素的个数 O(1)

4、queue(队列)

队列是一种先进先出的数据结构;

比喻性的描述可为 一条两端通透的隧道,火车车厢先进就先出,后进就后出;

头文件

#include <queue>

定义

queue<int>q;

方法函数

front()

返回队首元素 O(1)

back()

返回队尾元素 O(1)

push()

尾部添加一个元素副本 进队O(1)

pop()

删除第一个元素 出队 O(1)

size()

返回队列中元素个数,返回值类型unsigned int O(1)

empty()

判断是否为空,队列为空,返回true O(1)

5、deque(双端队列)

首尾都可插入和删除的队列为双端队列;

注意:deque可以进行排序,只能进行从小到大的排序;

头文件

#include <deque>

定义

deque<int>dq;

方法函数

back()/front()

访问(不删除)后/前端元素

push_back(x)/push_front(x)

把x压入后/前端

pop_back() /pop_front()

删除后/前端元素

erase(iterator it)

删除双端队列中的某一个元素

erase(iterator first,iterator last)

删除双端队列中[first,last)中的元素

empty()

判断deque是否空

size()

返回deque的元素数量

clear()

清空deque

6、list(链表)

头文件

#include <list>

定义

list<int> a;

// list的构造函数

list<int>a{1,2,3}

// 声明一个n个元素的列表,每个元素都是0

list<int>a(n)

// 声明一个n个元素的列表,每个元素都是m

list<int>a(n, m)

// 声明一个列表,其元素初值来源于由区间所指定序列中元素,first和last是迭代器

list<int>a(first, last)

方法函数

begin()/end()

通过调用list容器的成员函数begin()得到一个指向容器起始位置的iterator,可以调用list容器的end()函数来得到list末端下一位置

push_back(x)/push_front(x)

使用list的成员函数push_back和push_front插入一个元素到list中。其中push_back()是从list的末端插入,而push_front()是从list的头部插入

pop_back() /pop_front()

删除后/前端元素

empty()

判断list是否为空

erase(iterator first,iterator last)

删除双端队列中[first,last)中的元素

empty()

判断deque是否空

resize()

调用resize(n)将list的长度改为只容纳n个元素,超出的元素将被删除。如果n比list原来的长度长,那么默认超出的部分元素置为0。也可以用resize(n, m)的方式将超出的部分赋值为m

clear()

清空list中的所有元素

front()/back()

通过front()可以获得list容器中的头部元素,通过back()可以获得list容器的最后一个元素。注意:当list元素为空时,这时候调用front()和back()不会报错。因此在编写程序时,最好先调用empty()函数判断list是否为空,再调用front()/back()函数

pop_back()/pop_front()

使用pop_back()可以删掉尾部第一个元素,pop_front()可以删掉头部第一个元素。注意:list必须不为空,如果当list为空的时候调用pop_back()和pop_front()会使程序崩掉

assign()

assign(n, val):将a中的所有元素替换成n个val元素

a.assign(b.begin(), b.end())

list<int>a{6,7,8,9};

list<int>b{1,2,3,4,5};

b.assign(a.begin(),a.end());

// b中的元素变为6,7,8,9

swap()

交换两个链表。a.swap(b)和swap(a, b),都可以完成a链表和b链表的交换

reverse()

可以实现list的逆置

merge()

a.merge(b) 调用结束后b变为空,a中元素包含原来a和b的元素

insert()

在指定位置插入一个或多个元素

erase()

删除一个元素或一个区域的元素

remove()

从list中删除元素

7、pair(双元素模板结构)

pair只含有两个元素,可以看作是只有两个元素的结构体;

应用:

        代替二元结构体;

        作为map键值对进行插入;

头文件

#include <utility>

定义

// 带初始值

pair<string,int>p("wangyaqi",1);

// 不带初始值

pair<string,int>p;

p = {"wang",18};

访问

pair<int,int>p[20];

for(int i=0;i<20;i++) {

// 和结构体类似,first代表第一个元素,second代表第二个元素

cout<<p[i].first<<" "<<p[i].second;

}

8、map/unordered_map(关联映射)

template < class Key,                               // unordered_map::key_type

           class T,                                 // unordered_map::mapped_type

           class Hash = hash<Key>,                  // unordered_map::hasher

           class Pred = equal_to<Key>,              // unordered_map::key_equal

           class Alloc = allocator< pair<const Key,T> >  // unordered_map::allocator_type

           > class unordered_map;

map:按照键的顺序从小到大自动排序,内部实现为红黑树;

unordered_map:内部实现为哈希表,以便通过键值快速访问元素。

头文件

#include <map>

定义

map<string,string>mp;

map<string,int>mp;

map<int,node>mp; // node是结构体类型

方法函数

mp.find(key)

返回键为key的映射的迭代器 O(logN) 注意:用find函数来定位数据出现位置,它返回的一个迭代器,当数据出现时,它返回数据所在位置的迭代器,如果map中没有要查找的数据,它返回的迭代器等于end函数返回的迭代器

使用示例:

if (monitoredMap.find(path) == monitoredMap.end())

mp.erase(it)

删除迭代器对应的键和值O(1)

mp.erase(key)

根据映射的键删除键和值 O(logN)

mp.erase(first,last)

删除左闭右开区间迭代器对应的键和值 O(last-first)

mp.size()

返回映射的对数 O(1)

mp.clear()

清空map中的所有元素 O(N)

mp.insert()

插入元素,插入时要构造键值对

mp.empty()

如果map为空,返回true,否则返回false

mp.begin()

返回指向map第一个元素的迭代器(地址)

mp.end()

返回指向map尾部的迭代器(最后一个元素的下一个地址)

mp.rbegin()

返回指向map最后一个元素的迭代器(地址)

mp.rend()

返回指向map第一个元素的迭代器(地址)

添加元素

方式一:

mp["学习"] = "看书";

mp["玩耍"] = "打游戏";

方式二(插入元素构造键值对):

mp.insert(make_pair("vegetable","蔬菜"));

方式三:

mp.insert(pair<string,string>("fruit","水果"));

方式四:

mp.insert({"hahaha","wawawa"});

访问元素

mp["菜哇菜"] = "强哇强";

cout<<mp["菜哇菜"]<<"\n";

// ---------------------------------------遍历访问

方式一(迭代器访问):

map<string,string>::iterator it;

for(it=mp.begin();it!=mp.end();it++) {

     //      键                 值

     // it是结构体指针访问所以要用 -> 访问

     cout<<it->first<<" "<<it->second<<"\n";

     // *it是结构体变量 访问要用 . 访问

     // cout<<(*it).first<<" "<<(*it).second;

}

方式二(智能指针访问):

for(auto i:mp)

cout<<i.first<<" "<<i.second<<endl; // 键,值

方式三(对指定单个元素访问):

map<char,int>::iterator it = mp.find('a');

cout<<it -> first <<" "<<  it->second<<"\n";

9、multimap(关联映射-键对应多值)

multimap容器和map容器的区别在于,multimap容器中可以同时存储多个键相同的键值对;

template < class Key,                                   // 指定键(key)的类型

           class T,                                     // 指定值(value)的类型

           class Compare = less<Key>,                   // 指定排序规则

           class Alloc = allocator<pair<const Key,T> >  // 指定分配器对象的类型

           > class multimap;

和map容器一样,其中后2个参数都设有默认值,大多数时候只需设置前2个参数的值,有时候会用到第3个参数,最后一个参数几乎不会用到;

头文件

#include <map>

定义

multimap<string, int> mymultimap;

// 在创建multimap容器的同时,也可以进行初始化

multimap<string, int> mymultimap{{"penny",1},{"leonard",2},{"sheldon",3}};

// 借助pair类模板的构造函数生成各个pair类型的键值对

multimap<string,int> mymultimap{

    pair<string,int>{"penny",1},

    pair<string,int>{"leonard",2},

    pair<string,int>{"sheldon",3}

};

// 调用make_pair()函数,生成键值对元素;然后创建并初始化multimap容器

multimap<string,int> mymultimap{

    make_pair("penny", 1),

    make_pair("leonard",2),

    make_pair("sheldon",3)

};

// 拷贝(复制)构造函数,也可以初始化新的multimap容器

multimap<string,int> newmultimap(mymultimap);

// 升序(默认)

multimap<string,int> mymultimap{{"penny",1},{"leonard",2}};

multimap<string,int,less<string>> mymultimap{{"penny",1},{"leonard",2}};

// 降序

multimap<string,int,greater<string>> mymultimap{{"penny",1},{"leonard",2}};

方法函数

empty()

若容器为空,则返回true,否则返回false

size()

返回当前multimap容器中键值对的个数

max_size()

返回multimap容器所能容纳的键值对的最大个数,不同的操作系统,其返回值亦不同

count(key)

在当前multimap容器中,查找键为key的键值对的个数并返回

begin()

返回指向容器中第一个(已排好序的第一个)键值对的双向迭代器

end()

返回指向容器中最后一个元素(已排好序的最后一个)所在位置的后一个位置的双向迭代器

rbegin()

返回指向容器中最后一个(已排好序的最后一个)元素的反向双向迭代器

rend()

返回指向容器中第一个(已排好序的第一个)元素所在位置的前一个位置的反向双向迭代器

cbegin()

和begin()功能相同,只不过在其基础上,增加了const属性,不能用于修改容器内储存的键值对

cend()

和end()功能相同,只不过在其基础上,增加了const属性,不能用于修改容器内储存的键值对

crbegin()

和rbegin()功能相同,只不过在其基础上,增加了const属性,不能用于修改容器内储存的键值对

crend()

和rend()功能相同,只不过在其基础上,增加了const属性,不能用于修改容器内储存的键值对

find(key)

在map容器中查找键为key的键值对,若成功找到,则返回指向该键值对的双向迭代器;若未找到,则返回和end()方法一样的迭代器

lower_bound(key)

返回一个指向当前map容器中第一个大于或等于key的键值对的双向迭代器

upper_bound(key)

返回一个指向当前map容器中第一个大于key的键值对的双向迭代器

equal_range(key)

返回一个pair对象(包含2个双向迭代器),其中pair.first和lower_bound()方法的返回值等价,pair.second和upper_bound()方法的返回值等价。也就是说,该方法将返回一个范围,该范围中包含的键为key的键值对(map容器键值对唯一,因此该返回最多包含一个键值对)

insert()

向multimap容器中插入键值对

emplace()

在当前multimap容器中的指定位置处构造新键值对。其效果和插入键值对一样,但效率更高

emplace_hint()

在本质上和emplace()在multimap容器中构造新键值对的方式是一样的,不同之处在于,必须为该方法提供一个指示键值对生成位置的迭代器,并作为该方法的第一个参数

erase()

删除multimap容器指定位置、指定键(key)值或者指定区域内的键值对

clear()

清空multimap容器中的所有键值对

swap()

交换2个multimap容器中存储的键值对,操作的2个键值对的类型必须相同

二、迭代器(iterators)

迭代器提供顺序访问一个聚合对象中各个元素方法, 而又不需暴露该对象的内部表示;

迭代器实际上是一个指针,每种容器都有自己的迭代器,但是所有的迭代器的接口都是一样的,即所有的迭代器的操作方法都是一样的;

1、迭代器类型

Input Iterator:只能单步向前迭代元素,不允许修改由该类迭代器引用的元素;

Output Iterator:该类迭代器和Input Iterator极其相似,也只能单步向前迭代元素,不同的是该类迭代器对元素只有写的权力;

Forward Iterator:该类迭代器可以在一个正确的区间中进行读写操作,拥有Input Iterator的所有特性,和Output Iterator的部分特性,以及单步向前迭代元素的能力;

Bidirectional Iterator:该类迭代器是在Forward Iterator的基础上提供了单步向后迭代元素的能力;

Random Access Iterator:该类迭代器能完成上面所有迭代器的工作,它自己独有的特性就是可以像指针那样进行算术计算,而不是仅仅只有单步向前或向后迭代;

2、迭代器操作[begin,end)

所有迭代器

p++:后置自增迭代器

++p:前置自增迭代器

输入迭代器

*p:复引用迭代器,作为右值

p=p1:将一个迭代器赋给另一个迭代器

p==p1:比较迭代器的相等性

p!=p1:比较迭代器的不等性

输出迭代器

*p:复引用迭代器,作为左值

p=p1:将一个迭代器赋给另一个迭代器

正向迭代器

提供输入输出迭代器的所有功能

双向迭代器

--p:前置自减迭代器

p--:后置自减迭代器

随机迭代器

p+=i:将迭代器递增i位

p-=i:将迭代器递减i位

p+i:在p位加i位后的迭代器

p-i:在p位减i位后的迭代器

p[i]:返回p位元素偏离i位的元素引用

p<p1:如果迭代器p的位置在p1前,返回true,否则返回false

p<=p1:p的位置在p1的前面或同一位置时返回true,否则返回false

p>p1:如果迭代器p的位置在p1后,返回true,否则返回false

p>=p1:p的位置在p1的后面或同一位置时返回true,否则返回false

3、容器支持迭代器类别

vector

随机访问

deque

随机访问

list

双向

set

双向

multiset

双向 

map

双向

multimap

双向

stack

不支持

queue

不支持

priority_queue

不支持

三、算法(algorithms)

STL算法是对容器进行处理的函数;

算法主要是由头文件<algorithm、<functional>、<numeric>组成:

<algorithm>是所有STL头文件中最大的一个,范围涉及到比较、交换、查找、遍历操作、复制、修改等等;

<functional>定义了一些模板类,用以声明函数对象;

<numeric>体积很小,只包括几个在序列上面进行简单数学运算的模板函数;

1、查找类算法

find(beg,end val)

利用底层元素的等于操作符,对指定范围内的元素与输入值进行比较,当匹配时,结束搜索,返回该元素迭代器

#include <iostream>

#include <vector>

#include <algorithm> // 包含 std::find

int main() {

    // 示例驱动器字母向量

    std::vector<char> drives = {'A', 'B', 'C', 'E', 'F', 'G'};

    char targetDrive = 'D'; // 你想查找的驱动器字母

    // 使用 std::find 查找目标驱动器

    auto it = std::find(drives.begin(), drives.end(), targetDrive);

    if (it != drives.end()) {

        std::cout << "驱动器 " << targetDrive << " 存在于列表中。" << std::endl;

    } else {

        std::cout << "驱动器 " << targetDrive << " 不存在于列表中。" << std::endl;

    }

    return 0;

}

find_end(beg,end,beg2,end2)

在迭代区间[beg,end) 内查找与区间[beg2, end2) 内任意元素匹配的元素,然后返回迭代器,指向最后一个匹配的元素,如果找不到元素,则返回第一个范围的end迭代器

find_first_of(beg,end,beg2,end2)

与find_ first_ of类似,区别:查找最后一个匹配的元素

find_if(beg,end,val)

与find类似查找的不是一个值还是达成条件的值

2、排序类算法

sort(beg,end)

区间[beg,end) 内元素按字典次序排列

int arr[] = {5, 2, 9, 1, 5, 6};

int n = sizeof(arr) / sizeof(arr[0]);

std::sort(arr, arr + n);

for (int i = 0; i < n; ++i) {

std::cout << arr[i] << " ";

}

stable_ sort(beg, end, func)

同上,不过保存相等元素之间的顺序关系

sort(RectArr.begin(), RectArr.end(), comp);

bool comp(RotatedRect a, RotatedRect b)

{

    return a.center.x < b.center.x;

}

或者

sort(contours.begin(), contours.end(), [](const vector<Point>& a, const vector<Point>& b) {

 return contourArea(a) > contourArea(b);

});

partial_ sort (beg,mid, end)

将最小值顺序放在[beg,mid)内

random_ shuffle (beg,end)

区间内元素随意排序

reverse (beg,end)

将区间内元素反转

rotate(beg,mid ,end)

将区间[beg, mid) 和[mid, end)旋转,使mid为新的起点

merge(beg, end, beg2. end2,nbeg)

将有序区间[beg, end)和[beg2, end2)合 并到一个新的序列nbeg中,并对其排序

3、可变按序列算法

copy(beg,end, beg2)

将迭代区间[beg, end) 元素复制到以beg2 开始的区间

transform (beg, end, beg2, func)

功能同上,只是每个元素需要经过函数func处理

repl ace(beg, end, v1, v2)

将区间[beg,end)内等于 v1的元素替换为v2

fill (beg,end, v)

区间内元素都写入v

fill_ _n(beg, n, v)

从位置beg开始的n个元素写入v

generate (beg,n,rand)

向从beg开始的n个位置随机填写数据

remove (beg,end)

移除区间[beg,end)内的元素, 注意:并不真正删除

unique(beg,end)

剔除相邻重复的元素,注意:并不真正删除

4、关系算法

equal (beg, end, beg2,end2)

判断两个区间元素是否相等

includes (beg,end,beg2,end2)

判断[beg,end) 序列是否被第二个序列[beg2, end2) 包含

max_ element (beg, en)

返回序列最大元素的位置

min_ element (beg, end)

返回序列最小元素的位置

mismatch (beg,end,beg2,end2)

查找两个序列中第- -一个不匹配的元素,返回一对iterator,标记第一个不匹配元素的位置

5、堆算法

make_ heap (beg,end)

以区间[beg,end)内元素建立堆

pop_ heap (beg,end)

重新排序堆,使第-一个与最后- 一个交换,并不真正弹出最大值

push_ heap(beg, end)

重新排序堆,把新元素放在最后一个位置

sort_ heap(beg, end)

对序列重新排序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值