STL初始
STL基本概念
-
STL(Standard Template Library)标准模板库
-
STL从广义上分,容器(container)、算法(algorithm)、迭代器(iterator)
-
容器和算法之间通过迭代器进行无缝连接
-
STL几乎所有代码都采用了模板类或者模板函数
函数参数范围都是左闭右开区间
STL六大组件
-
容器:各种数据结构,如vector、list、deque、set、map等,用来存放数据
-
算法:各种常用算法,如sort、find、copy、for_each等
-
迭代器:扮演了容器与算法之间的胶合剂
-
仿函数:行为类似函数,可作为算法的某种策略
-
适配器:一种用来修饰容器或者仿函数或迭代器接口的东西
-
空间适配器:负责空间的配置与管理
STL中的容器、算法、迭代器
容器:置物之所也 就是将运用最广泛的一些数据结构实现出来 常用的数据结构:数组,链表,树,栈,队列,集合,映射表等 这些容器分为序列式容器和关联式容器 序列式容器:强调值的排序,序列式容器中的每个元素均有固定的位置 关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系
算法:问题之解也 有限的步骤,解决逻辑或数学上的问题,这一门学科叫算法 算法分为:质变算法和非质变算法 质变算法:是指运算过程中会更改区间内的元素的内容。如拷贝,替换,删除等 非质变算法:是指运算过程中不会更改区间内的元素内容,例如查找,计数遍历,找极值等
迭代器:提供一种方法,能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部方式 每个容器都有自己专属的迭代器 迭代器使用非常类似于指针
常用的容器中迭代器种类为双向迭代器,和随机访问迭代器
容器算法迭代器初识
vector存放内置数据类型
容器:vector 算法:for_each 迭代器:vector<int>::iterator
#include<iostream> #include<vector> #include<algorithm> using namespace std; void MyPrint(int _val) { cout << _val << endl; } void Test01() { vector<int> v; v.push_back(10); v.push_back(20); vector<int>::iterator pb = v.begin(); vector<int>::iterator pe = v.end(); //第一种遍历 while(pb != pe) { cout << *pb << endl; pb++; } //第二种 for(vector<int>::iterator it = v.begin(); it != v.end(); it++) { cout << *it << endl; } //第三种,#include<algorithm> for_each(v.begin(), v.end(), MyPrint); } int main() { Test01(); }
vector存放自定义数据类型
#include<iostream> #include<vector> #include<string> using namespace std; class Person() { public: Preson(string _name, int _age) { mName = _name; mAge = _age; } public: string mName; int mAge; } void Test01() { vector<Preson> v; Preson p1("a", 1); Preson p2("b", 2); v.push_back(p1); v.push_back(p2); for(vector<Preson>::iterator it = v.begin(); it != v.end(); it++) { cout << (*it).mName << (*it).mAge <<endl; } } void Test02() { vector<*Preson> v; Preson p1("a", 1); Preson p2("b", 2); v.push_back(p1); v.push_back(p2); for(vector<*Preson>::iterator it = v.begin(); it != v.end(); it++) { cout << (**it).mName << (**it).mAge <<endl; } } int main() { Test01(); Test02(); }
vector容器嵌套容器
vector<vector<int>> v; vector<int> v1; vector<int> v2; for(int i = 0; i < 2; i++) { v1.push_back(i + 1); v2.push_back(i + 2); } for(vector<vector<int>>::iterator it = v.begin(); it != v.end(); it++) { for(vector<int>::iterator vit = (*it).begin(); vit != (*it).end(); vit++) { cout << *vit << " "; } }
STL常用容器
string容器
构造函数
-
string(); 创建一个空字符串
-
string(const char* s); 使用字符串s初始化
-
string(const string& s); 使用string对象初始化另一个string对象
-
string(int n, char c); 使用n个字符c初始化
#include<string> string s1; string s2("aaa"); char* str = "bbb"; string s3(str); string s4(10, 'a');
赋值=, assign
string str1.assign("aaa"); string str2.assign("bbbbbb", 3); string str3.assign(str1); string str4.assign(5, "a" );
拼接+, append
查找和替换find,replace
rfind从右往左查,find从左往右查。下标位置都是从左开始 替换,会将整个字符串替换进去
string s1 = "111111111"; s1.replace(0, 3, "222"); //222111111111
字符串比较compare
字符串比较是按字符的ASCII码进行对比 =返回0
返回1 <返回-1
有第一个不同的就会结束比较,一般用来判断两个字符串是否相等
字符存取[],at
字符串插入和删除insert,erase
起始下标从0开始
子串获取substr
vector容器
构造函数
vector数据结构和数组非常相似,也称为单端数组 数组是静态空间,vector是动态空间 动态扩展:并不是在原空间之后续接新空间,而是找更大的空间,然后将原数据拷贝新空间,释放原空间 跟realloc有点像
vector容器的迭代器是支持随机访问的迭代器
vector<int> v1; for(int i = 0; i < 10; i++) { v1.push_back(i); } for(vector<int>::iterator it = v1.begin(); it != v1.end(); it++) { cout << *it << " "; } vector<int> v2(v1.begin(), v1.end()); vector<int> v3(10, 100); vector<int> v4(v1);
赋值操作=, assign
容量和大小empty, capacity, size, resize
缩小大小,容量不会变化
vector插入和删除insert, push_back, pop_back, erase, clear
数据存取[], at, front, back
互换容器swap
vecto<int> v1; vector<int> v2; v1.swap(v2); //收缩内存 vector<int>(v1).swap(v1) //匿名对象,执行完,系统会回收内存
预留空间reserve
减少vector在动态扩展容量时的扩展次数
vector<int> v; int* p = NULL; int num = 0; //计算动态扩展了多少次空间 for(int i = 0; i < 100000; i++) { v.push_back(i); if(p != &v[0]) { p = &v[0]; ++num; } } v.reserve(100000);
deque容器
构造函数
双端数组,可以对头端进行插入删除操作 deque与vector区别
-
vector对于头部插入删除效率低,数据量越大,效率越低
-
deque,对头部的插入删除操作更快
-
vector访问元素时的速度更快,与两者的内部实现有关
内部工作原理:
deque的迭代器也是支持随机访问的
void Print(const deque<int>& d) { for(deque<int>::const_iterator it = d.begin(); it != d.end(); it++) { cout << *it << " "; } }
赋值=,assign
大小操作empty,size,resize
跟vector比没有容量的概念
插入和删除
数据存取at,[],front,back
deque排序sort
利用算法实现对deque容器进行排序0 需要头文件, algorithm 默认从小到大排序,升序
stack容器
基本概念
FILO,先进后出。栈中只有顶端元素才可以被外界使用,因此栈不允许有遍历行为
常用接口
queue容器
基本概念
先进先出,FIFO,有两个出口
常用接口
list容器
构造函数
将数据进行链式储存 链表是一种物理储存单元上非连续性的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的 链表由一系列结点组成 结点:数据域和指针域 STL中的链表是双向循环链表
链表list中的迭代器只支持前移和后移,属于双向迭代器 优点:
-
采用动态存储分配,不会造成内存浪费和溢出
-
齿形插入和删除操作十分方便,修改指针即可,不需要移动大量元素
缺点:
-
链表灵活,但空间(指针域)和时间(遍历)额外耗费较大
list插入删除操作都不会造成原有list迭代器失效,这在vector是不成立的,vector会寻找新的空间
赋值和交换assign,=,swap
大小操作size,empty,rsize
插入和删除
可以直接删除某个值
数据存取front,back
反转和排序reverse,sort
bool MyCompare(int val1, int val2) { return val1 > val2; } List<int> L; L.reverse(); L.sort(); //默认从小到大 L.sort(MyCompare); //设定为从大到小排序
set / multiset 容器(集合)
构造和赋值
所有元素插入时自动被排序 set/multiset属于关联式容器,底层结构二叉树实现 只用包含set头文件 set和multiset区别:
-
set不允许容器中有重复元素
-
multise中允许容器中有重复元素
大小和交换size,empty,swap
插入和删除insert,clear,erase
插入只有insert! 可以直接删除某个值
查找和统计find,count
count对于set只会是0或1,对multiset不一定
set<int> s1; set<int>::iterator pos = s1.find(30);
set和multiset的区别
set<int> s; pair<set<int>::iterator, bool> ret = s.insert(10); if(set.second) { cout << "Successful insertion"; } multiset<int> ms; ms.insert(10); ms.insert(10);
pair对组的创建
成对出现的数据,利用对组可返回两个数据
pair<string, int> p(string("Tom"), 20); cout << p.first << p.second; pair<string, int> p2 = make_pair("Jerry", 10); cout << p2.first << p2.second;
内置类型指定排序规则
利用仿函数,可以改变排序规则
class MyCompare() { public: bool operator()(int v1, int v2) { return v1 > v2; } }; set<int, MyCompare> s; s.insert(10); s.insert(220); s.insert(50); for(set<int, MyCompare>::iterator it = s.begin(); it != s.end(); it++) { cout << *it << " "; }
自定义数据类型指定排序规则
class Person { public: Person(string _name, int _age) { name = _name; age = _age; } string name; int age; }; class MyCompare() { public: bool operator()(const Person& p1, const Person& p2) { return p1.age > p2.age; } }; set<Person, MyCompare> s; s.insert("鲁路修", 16); s.insert("C.C.", 500); for(set<Person, MyCompare>::iterator it = s.begin(); it != s.end(); it++) { cout << *it.name << *it.age; }
map / multimap容器
构造和赋值
高性能,高效率 简介:
-
map中所有元素都是pair
-
pair中第一个元素为key(键值),起到索引作用,第二个元素为value(实值)
-
所有元素都会根据元素的键值自动排序
本质:
-
map/multimap属于关联式容器,底层结构是用二叉树实现
优点:
-
可以根据key值快速找到value值
map和multimap的区别:
-
map不允许容器中有重复key值元素
-
multimap允许容器中有重复key值元素
大小和交换size,empty,swap
插入和删除insert,erase,clear
map<int, int> m; m.insert(pair<int, int>(1, 10)); m.insert(make_pair(3, 10)); m.insert(map<int, int>::value_type(3, 30)); m[4] = 40; m.erase(m.begin()); m.erase(3);
通过[]访问不存在的键值,会创建新的键值对,键值为该访问的键值,实值为0
查找和统计find,count
函数原型:
-
find(key); //查找key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回map.end();
-
count(key); //统计key的元素个数
map<int, int> m; m.insert(pair<int, int>(1, 10)); m.insert(pair<int, int>(2, 10)); m.insert(pair<int, int>(3, 10)); map<int, int>::iteraror pos = m.find(3); if(pos != m.end()) { cout << "yes"; } else { cout << "no"; }
排序
依旧利用仿函数,更改为自动排序为从大到小排序
class MyCompare() { public: bool operator()(int v1, int v2) { return v1 > v2; } } ... for(map<int, int, MyCompare>::iterator it = m.begin(); it != m.end(); it++) { cout << it -> first << it -> second; }
STL函数对象
函数对象
class MyAdd { public: int operator()(int v1, int v2) { return v1 + v2; } } MyAdd myAdd; cout << myAdd(1, 2); class MyPrint { public: MyPrint() { count = 0; } void operator()(string s) { cout << s << endl; count++; } int count; //内部自己的状态 } Myprint myPrint; myPrint("纯血的魔女久远寺有珠"); cout << myPrint.count; void Test(MyPrint& mp, string s) { mp(s); } MyPrint mp; Test(mp, "不要小看人类的觉悟啊");
谓词
一元谓词
使用find_if需要#include<algroithm>
二元谓词
内建函数对象
内建函数对象的意义
算数仿函数
关系仿函数
#include<vector> #include<functional> #include<algorithm> vector<int> v; sort(v.begin(), v.end(), greater<nt>());
逻辑仿函数
vector<bool> v1; v1.push_back(false); v1.push_back(true); vector<bool> v2; v2.resize(v1.size()); //全部取反了 transform(v1.begin(), v1.end(), v2.begin(), logical_not<bool>());
常用算法
概述
-
算法主要是由头文件<algorithm> <functional> <numeric> 组成
-
<algorithm> 是所有STL头文件中最大的一个,范围涉及比较、交换、查找、遍历操作、复制、修改等等
-
<numeric> 体积很小,只包括几个在序列上面进行简单数学运算的函数模板
-
<functional> 定义了一些模板类,用以声明函数对象
遍历算法
for_each 遍历
void Print1(int val) { cout << val; } class Print2 { public: void operator()(int val) { cout << val; } } for_each(v.begin(), v.end(), Print1); for_each(v.begin(), v.end(), Print2());
transform 搬运
搬运
class Transform { public: int operator()(int val) { return val; //除了返回值外,还可以附加其他操作 } } vecter<int> v; v.push_bacK(1); v.push_back(2); vector<int> v2; v2.resize(v.size()); //提前开辟空间 transform(v.begin(), v.end(), v2.begin(), Transform());
查找算法
find 按值查找
find_if 按条件查找
class Compare { public: bool operator()(int val) { return val > 5; } } find_if(v.begin(), v.end(), Compare())
adjacent_find 查找相邻重复
binary_search 查找某值是否存在,返回布尔值
count 统计个数
class Person { public: Person(string _name) { name = _name; } bool operator==(const Person& p) { if(name == p.name) { return true; } else { return false; } } string name; } vector<Person> v; Person p1("阿尔托莉雅"); Person p2("冠位人偶师"); Person p3("草食狼"); v.push_back(p1); v.push_back(p2); v.push_back(p3); Person p4("咕哒"); int num = count(v.begin(), v.end(), p4); cout << "num = " << num;
count_if 按条件统计个数
排序算法
sort 排序,默认小到大
random_shuffle 随机打乱顺序
记得加随机数种子 srand((unsigned int)time(NULL));
merge 合并容器
合并完的容器仍然是有序的 目标容器需要提前开辟空间,大小为两个源容器大小的和
reverse 反转
拷贝和替换算法
copy 范围内元素拷贝
目标容器需要提前设置大小
replace 范围内某元素替换
replace_if 满足条件的范围内元素替换
swap 交换
交换的容器要同种类型
算数生成算法
使用头文件#include <numeric>
accumulate 计算容器元素累计总和
fill 向容器中添加元素
一般用来进行后期填充
集合算法
set_intersection 求交集
vTarget.resize(min(v1, v2)); vector<int>::inerator itEnd = set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), vTarget.begin()) for_each(vTarget.begin(), itEnd, myPrint());
重设大小为两个容器中较小的那个即可
set_union 求并集
vT.resize(v1.size() + v2.size()); vector<int>::iterator itEnd = set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), vTarget.begin()); for_each(vT.begin(), itEnd, myPrint);
set_difference 求差集
//v1 和 v2 的差集 vT.resize(max(v1.size(), v2.size())); vector<int>::iterator it = set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), vTarget.begin()); //v2 和 v1 的差集 vector<int>::iterator it2 = set_difference(v2.begin(), v2.end(), v1.begin(), v1.end(), vTarget.begin());