STL 黑马程序员教程
1 概述
1 .1 STL基本概念
- STL(Standard Template Library)标准模板库
- STL广义上分为:容器(container)、算法(algorithm)、迭代器(iterator)
- container 和 algorithm之间通过iterator连接
- STL采用了模板类和模板函数
1.2 STL六大组件
容器、算法、迭代器、仿函数、配置器(配接器),空间配置器
-
容器:
- 序列式容器:每个元素有固定的位置
- 关联式容器:二叉树结构,元素没有严格物理上的顺序关系
-
算法:
- 质变算法:运算过程中会更改区间内的元素的内容,另一个就是非质变算法
-
迭代器:每个容器有自己专属的迭代器,其中常用的是双向迭代器和随机访问迭代器
2 String
- 构造函数
- 赋值函数 :①等号重载 ②assign()
- 字符串拼接:①+= ②append()
- 查找:①find(),从左到右查 ②rfind(),从右到左查 ----最后返回的下标都是从左到右的
- 替换:
S1.replace(int pos,int n, s2)
- 字符串比较
s1.compare(s2)
- string中单个字符的存取:①直接用下标
S1[INDEX]
,②s1.at(index)
- 插入与删除:
s1.insert(int pos,int n,string)
s1.erase(int pos,int n)
- 子串:
string substr (int pos=0,int n=npos)
从[pos,n]的左闭右闭切片
//打印字符串的时候,注意要用c_str(),不然会有乱码 string a ="cjy"; printf("%s",a.c_str()); //c_str()返回当前字符串的首字符地址 //当然,直接按下面这也行 cout<<a<<endl;
3 Vector
-
构造函数:
- 默认使用模板:
vector<T> v
,无参构造,后面自己push_back
元素进去 vector(v.begin(),v.end())
,将begin和end间的元素拷贝- 拷贝构造
- 默认使用模板:
-
赋值:①重载等号②
v.assign(beg ,end)
③v.assign(n,elem)
拷贝n个element -
empty()
-
capacity() 容量
-
size() 当前大小
// 遍历vector容器的方法 vector<int> a; // 插入 for( int i = 0; i < 10; i++) push_back(i); // 遍历 for(vector<int>::iterator i = a.begin(); i != a.end(); i++) cout<< *i <<" "; for(auto i = a.begin(); i != a.end(); i++) cout<<*i <<" "; for(auto x : a) cout<<x<<" "; // 支持比较大小,从第一个元素开始依次 vector<int> a(3,4), b(4,3); if(a<b) puts("Yes");
-
resize (int num (elem))
,变短删去末尾多的,变长用默认值(elem
)填充 -
插入删除
- push_back()
- pop_back()
- insert(const_iterator pos) 注意insert之后,这里的迭代器位置会发生变化,但仍然指向原来元素
- erase(const_iterator pos)
- clear() 删除所有元素
-
数据存取
at[IDX]
- operator[]
- front() ----返回容器第一个元素
- back() -----返回容器最后一个元素
-
互换容器
v1.swap(v2)
//一个小小的语法糖 //假设创建了一个很大容量的v1 vector<int> v(100000); v.resize(3); //这个时候 哪怕resize了,capacity不变,只是size变了 //这时候就会浪费很多空间 //通过与匿名对象swap,更改capacity,节约空间 vector<int>(v).swap(v) 先用
vector<int>(v)
拷贝构造了一个匿名对象,这个对象与vswap
之后,capacity和size都变为3。 -
预留位置
reserve(int len);
//容器预留len个元素长度,预留位置不初始化,元素不可访问。- 主要是因为vector有个动态扩展长度的功能,如果之前开辟空间不够,他会重新自动分配空间给整个vector。预留空间,可以节省这个过程
4 deque(双端数组)
-
deque头部访问速度比vector快
-
deque内部有个中控器,维护每段缓冲区中的内容,缓冲区中存放真实数据
-
中控器维护的是每个缓冲区的地址,使得使用deque时像一片连续的内存空间
-
deque容器的迭代器也是支持随机访问的
- 构造函数:和vector一样
- 赋值:①重载=②
assign(begin,end)
③`` empty() size() resize()
deque没有capacity的概念- deque数据插入与删除
- push_back(elem) push_front(elem) pop_back pop_front
- insert(const_iterator pos,elem) clear() erase()
- 数据存取:
d.at(index)
- operator[]
- d.front() --返回第一个元素
- d.back() --返回最后一个元素
- deque排序:
sort(iterator beg, iterator end)
---对beg和end区间内元素进行排序
5 stack(FILO)
- 构造函数
stack<T> stk
拷贝构造stack(stk)
- 赋值操作:重载
- 数据存取:
push(elem) pop() top()
- 大小:
empty() size()
6 queue (FIFO)
只允许一端进,另一端出
- 构造函数
- 重载等号赋值
push(elem) pop() back()--取最后一个元素 front() ---取第一个元素
empty() size()
7 List (双向循环链表)
双向链表采用双向迭代器,支持前移后移
-
构造函数:和vector一样
-
赋值:
assign(beg,end) assign(n,elem) = swap
-
size() empty() resize(num) resize(num,elem)
-
插入与删除
- push_back(elem);//在容器尾部加入一个元素
- pop_back();//删除容器中最后一个元素
- push_front(elem);//在容器开头插入一个元素
- pop_front();//从容器开头移除第一个元素
- insert(pos,elem);//在pos位置插elem元素的拷贝,返回新数据的位置。
- insert(pos,n,elem);//在pos位置插入n个elem数据,无返回值。
- insert(pos,beg,end);//在pos位置插入[beg,end)区间的数据,无返回值。
- clear();//移除容器的所有数据
- erase(beg,end);//删除[beg,end)区间的数据,返回下一个数据的位置。
- erase(pos);//删除pos位置的数据,返回下一个数据的位置。
- remove(elem);//删除容器中所有与elem值匹配的元素。 A
- 存取元素:
front()返回第一个元素 back()返回最后一个元素
不能用at和【】 原因是迭代器不是随机访问迭代器,而是双向迭代器
- 存取元素:
-
成员函数:
L.reverse()翻转链表 L.sort()排序
注意:所有不支持随机访问迭代器的容器,不可以用标准算法,这些迭代器内部会提供相应的成员函数
默认采用升序序列排序,要通过函数或仿函数自定义相应的排序规则
比如 我要写一个降序的回调函数
bool mycompare(int v1,int v2) { //降序 让第一个数大于第二个数 return v1>v2; }
8 Set/multiset ----头文件<set>
- 所有元素在插入时会自动排序,是个关联式容器,底层是以二叉树结构实现
-
构造:
set<T> st set()拷贝构造
-
等号赋值
-
数据插入用insert
st.insert(elem)
clear() erase(pos) erase(beg,end) erase(elem)
-
size() empty() swap(st)
-
find(key) 查找key是否存在,若存在则返回该键的元素的迭代器,若不存在,返回end迭代器
-
count(key) 统计某个key元素的个数 (对set而言,结果要么0,要么1,因为不允许重复)
-
set和multiset的区别:
-
set不可以插入重复数据,而multiset可以
-
set插入数据的同时会返回插入结果,表示插入是否成功,以对组形式返回
pair<set<int>::iterator ,bool>
,如果插入成功,迭代器指向插入位置,bool值为true -
multiset不会检测数据,直接返回迭代器,因此可以插入重复数据
-
set容器排序:set容器默认排序规则为从小到大,利用仿函数改变排序规则(降序或者是自定义数据类型排序时使用)
尤其是自定义数据类型,如果不给排序规则,就不能插入set,因为编译器会报错
//比如我要插入5,4,3,2,1 ;如果直接插入set,是个升序排序,这时候我们要用仿函数 //定义一个仿函数 class myCompare{ public: //仿函数简单理解为重载小括号 bool operator()(int v1, int v2) { return v1>V2; } }; int main(){ //创建容器的时候带上仿函数的类名即可 set<int,myCompare> s1; s1.insert(1); //后面就随便插入了,会自动根据仿函数规则排序的 }
-
9 pair对组
成对出现的数据,也支持按字典序进行比较,比较时以first为第一关键字,以second为第二关键字
创建方式:
pair<type,type> p (value1,value2)
pair<type,type> p =make_pair(value1,value2)
p = {value1, value2}
打印数据:p.first p.second
注意这是成员,不是函数
10 map/multimap
简介:
- map中所有元素都是pair,所以插入元素都是对组
- pair中第一个元素为key(键值),起到索引作用,第二个元素为value(实值)
- 所有元素都会根据元素的键值自动排序
本质:
- map/multimap属于关联式容器,底层结构是用二叉树实现。
优点:
- 可以根据key值快速找到value值
map和multimap区别:
- map不允许容器中有重复key值元素
- multimap允许容器中有重复key值元素
-
构造:
map<T1,T2> mp map(const map&mp)拷贝构造
-
重载等号赋值
-
size(); empty(); swap(st);
-
插入删除数据:
-
insert(pair<int,int>(10,20))
-
clear()
-
erase(pos迭代器)
-
erase(bed,end) -----返回下一个元素的迭代器
-
erase(key) ----删除值为key的元素
-
查找与统计:
-
find(键)
----若键存在返回该键的元素的迭代器 -
count(key)
统计元素个数 -
排序:同set,自定义仿函数进行排序
STL-函数对象/仿函数
1.概念:
-
重载__函数调用操作符__的类,其对象被称为函数对象
-
函数对象使用重载的
()
时,因为其行为像个函数,所以也叫仿函数 -
本质:仿函数本质是个类,不是一个函数
class MyAdd{ public: int operator()(int a,int b) { return a+b; } }; void test01() //可以像函数一样传参,有返回值 { MyAdd myadd; //因为是个类,还是要先实例化对象 cout<<myadd(10,10)<<endl; }
因为本质上是个类,所以可以有自己的成员变量来记录状态
class MyAdd{ public: int operator()(int a ,int b) { return a+b; } int cnt=0 //记录调用次数 };
仿函数可以做为函数参数传递 类似python中的lambda函数
void doAdd(MyAdd &m) { cout<<m.cnt<<endl; } void test03(){ MyAdd myadd; doAdd(myadd); }
2. 谓词
2.1 谓词的概念
- __返回bool类型的仿函数__称为谓词
- 如果operator()接受一个参数,就叫做一元谓词
- 如果接受两个参数,就叫二元谓词
一元谓词举例
class GreaterFive{ bool operator()(int val) //只有一个参数 { return val>5; } };
3. 内建函数对象
引入头文件#include<functional>
STL内建了一些函数对象,分为算术仿函数、关系仿函数、逻辑仿函数
这些仿函数产生对象,用法与一般函数完全相同。
3.1 算术仿函数
功能描述:
- 实现四则运算
- 其中negate是一元运算,其他都是二元运算
仿函数原型:
template<class T> T plus<T>
//加法仿函数template<class T> T minus<T>
//减法仿函数template<class T> T multiplies<T>
//乘法仿函数template<class T> T divides<T>
//除法仿函数template<class T> T modulus<T>
//取模仿函数template<class T> T negate<T>
//取反仿函数
调用过程
void test() { //创建仿函数对象 negate<int> a; plus<int> b; //像调用函数一样去使用 cout<<a(50)<<endl; cout<<b(20,10)<<endl; }
3.2 关系仿函数
仿函数原型:
template<class T> bool equal_to<T>
//等于template<class T> bool not_equal_to<T>
//不等于template<class T> bool greater<T>
//大于template<class T> bool greater_equal<T>
//大于等于template<class T> bool less<T>
//小于template<class T> bool less_equal<T>
//小于等于
3.3 逻辑仿函数
函数原型:
template<class T> bool logical_and<T>
//逻辑与template<class T> bool logical_or<T>
//逻辑或template<class T> bool logical_not<T>
//逻辑非
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步