STL初识
一、STL初识
容器、算法、迭代器
容器和算法之前通过迭代器无缝连接
STL几乎所有的代码都采用了类模板和函数模板
STL六大组件
容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器
1.容器:各种数据结构,如vector,list,deque,set,map等,用来存放数据
2.算法:各种常用算法,如sort,find,copy,for_each
3.迭代器:扮演了容器和算法之间的胶合剂
4.仿函数:行为类似函数,可作为算法的某种策略
5.适配器:一种用来修饰容器或者仿函数或迭代器接口的东西
6.空间配置器:负责空间的配置和管理
STL中容器、算法、迭代器
容器:序列式和关联式容器:
- 序列式容器:强调值的排序,序列式容器中的每个元素都有固定的位置
- 关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系
算法:质变算法和非质变算法:
- 质变算法:运算过程中会更改区间内的元素的内容。例如拷贝,替换,删除等等
- 非质变算法:运算过程中不会更改区间内的元素内容,例如查找、计数、遍历、寻找极值等
迭代器:容器和算法之间的粘合剂
- 提供一种方法,使之能够依序访问某个容器所含的各个元素,而无需暴露该容器的内部表示方式。
- 每个容器都有自己的专属迭代器
- 迭代器类似指针
迭代器种类:
种类 | 功能 | 支持运算 |
---|---|---|
输入迭代器 | 只读 | ++、==、!= |
输出迭代器 | 只写 | ++ |
前向迭代器 | 读写、向前递推 | ++、==、!= |
双向迭代器 | 读写、前后推进 | ++、-- |
随机访问迭代器 | 读写、跳跃式访问 | ++,--,[n],-n,<,<=,>,>= |
常用迭代器为双向迭代器,随机访问迭代器
二、容器算法迭代器初识
1.vector容器存放内置数据类型
容器:vector
算法:for_each
迭代器:vector<int>::iterator
三种遍历算法
#include <vector>
vector<int> mv;
vector<int>::iterator itBegin = mv.begin();//起始迭代器,指容器中第一个元素
vector<int>::iterator itEnd = mv.end();//结束迭代器 指向容器中最后一个元素的下一个位置
while (itBegin!=itEnd)//第一种遍历方式
{
cout << *itBegin << endl;
itBegin++;
}
for (vector<int>::iterator it = mv.begin();it!=mv.end();it++)//第二种遍历方式
{
cout << *it << endl;
}
#include <algorithm>
void myPrint(int i)
{
cout << i << endl;
}
//调用
...
vector<int> mv;//创建容器
mv.push_back(10);//尾插入数据
for_each(mv.begin(), mv.end(), myPrint);//STL提供的遍历算法
...
2.vector容器中存放自定义的数据类型
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;
//vector容器存放自定义数据类型
class Person
{
public:
string m_Name;
int m_Age;
Person(string name, int age)
{
this->m_Name = name;
this->m_Age = age;
}
};
void myPrint(Person p)
{
cout << p.m_Name << p.m_Age << endl;
}
void test1()
{
vector<Person> mv;
Person p1("aaa", 10);
Person p2("vvv", 20);
Person p3("sss", 30);
mv.push_back(p1);
mv.push_back(p2);
mv.push_back(p3);
for_each(mv.begin(), mv.end(), myPrint);//STL提供的遍历算法
}
int main()
{
test1();
system("pause");
}
3.vector容器嵌套
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;
void myPrintv(int i)
{
cout << i << endl;
}
void myPrint(vector<int> mv)
{
for_each(mv.begin(), mv.end(), myPrintv);//STL提供的遍历算法
}
void test1()
{
vector<vector<int>> mv;
vector<int> mv1;
vector<int> mv2;
vector<int> mv3;
for (int i=0; i<4 ; i++)
{
mv1.push_back(i+1);
mv2.push_back(i+2);
mv3.push_back(i+3);
}
//将小容器插入大容器中
mv.push_back(mv1);
mv.push_back(mv2);
mv.push_back(mv3);
for_each(mv.begin(), mv.end(), myPrint);//STL提供的遍历算法
}
int main()
{
test1();
system("pause");
}
三、vector容器
功能:
- vector数据结构和数组非常相似,也成为单端数据
- vector可以动态扩展、不是在原空间之后接续新空间,而是找到更大的内存空间,然后将源数据拷贝新空间,释放原空间
- vector迭代器支持随机访问
1.vector构造函数
创建vector容器
函数原型
vector<T> mv; //采用模板实现类实现,默认构造函数
vector(mv.begin(), mv.end()); //将mv [ begin(),end() )区间中的元素拷贝给本身、 左闭右开
vector(n,elem); //构造函数,将n个elem拷贝给本身
vector(const vector &vec); //拷贝构造
2.赋值操作
vector& operator=(const vector &vec);//等号重载
vec1.assign(vec2.beg,vec2.end); //将[ vec2.begin, vec2.end ) 区间中的数据拷贝复制给本身、并清空vec1原来的数组(如果有的话)
assign(n,elem); //将n个elem拷贝复制给本身
3.容量和大小
empty(); //判断容器是否为空
capacity(); //容器的容量 >= size
size(); //返回容器中元素的个数
resize(int num);//重新制定容器的长度为num,若容器边长,则以默认值填充新位置、以0填充其他位置
//若容器变短,则末尾超出容器长度的元素被删除
resize(int num, elem);
4.vector插入和删除
push_back(elem); //尾部插入元素elem
pop_back(); //删除最后一个元素
insert(const_iterator pos, elem);//迭代器向指定位置pos插入元素elem
insert(const_iterator pos, int count, elem);//迭代器指定位置pos插入count个元素elem
erase(const_iterator pos);//删除迭代器指向的元素
erase(const_iterator start, const_iterator end);//删除迭代器从start到end之间的元素
clear(); //删除容器中所有元素
vector<int> mv;
vector<int>::iterator itBegin = mv.begin();//起始迭代器,指容器中第一个元素
5.vector数据存取
at(int index); //返回index所指的数据
operator([]); //返回索引indx
front(); //返回容器中第一个数据元素
back(); //返回容器中最后一个数据元素
vector<int> mv;
int temp = mv.front();
6.swap(vec)互换容器
std::vector::swap(vec);//将vec与本身的元素互换,
使用vector收缩内存空间
使用resize
将原本存储占用更大空间的数据,会发现有很多空间被闲置
#include <iostream>
#include <vector>
void test03()
{
vector<int> v1;
for (int i = 0; i < 100000; i++)
{
v1.push_back(i);
}
cout << "v1的容量为:" << v1.capacity() << endl;
cout << "v1的大小为: " << v1.size() << endl;
v1.resize(3);
cout << "v1的容量为:" << v1.capacity() << endl;
cout << "v1的大小为: " << v1.size() << endl;
//现在v1中只存在3个数据,但是却占用了很大的一段内存
}
int main()
{
test03();
}
结果
v1的容量为:138255
v1的大小为: 100000
v1的容量为:138255
v1的大小为: 3
方法
-vector<int>(v1).swap(v1);
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;
void test03()
{
vector<int> v1;
for (int i = 0; i < 100000; i++)
{
v1.push_back(i);
}
cout << "v1的容量为:" << v1.capacity() << endl;
cout << "v1的大小为: " << v1.size() << endl;
v1.resize(3);
cout << "v1的容量为:" << v1.capacity() << endl;
cout << "v1的大小为: " << v1.size() << endl;
cout << endl << "收缩内存后:" << endl;
vector<int>(v1).swap(v1);
cout << "v1的容量为:" << v1.capacity() << endl;
cout << "v1的大小为: " << v1.size() << endl;
}
int main()
{
test03();
system("pause");
}
结果
v1的容量为:138255
v1的大小为: 100000
v1的容量为:138255
v1的大小为: 3
收缩内存后:
v1的容量为:3
v1的大小为: 3
std::vector::shrink_to_fit()-减少容器的容量以适应其大小并销毁超出容量的所有元素。
std::vector<int> sss={1,2,3,4};
sss.shrink_to_fit();
7.vector预留空间
std::vector::reserve(int len);//容器预留len个元素长度,预留位置不初始化,元素不可访问。
四、deque容器
- 双端数组,可以对头端进行插入删除操作
1.deque和vector的区别
- vector对于头部的插入删除效率低,数据量大,效率越低、需要移动元素
- deque相对而言,对头部的插入删除速度比vector快
- vector访问元素的速度会比deque快,这和两者内部实现有关
- deque容器的迭代器也是支持随机访问的
2.deque内部工作原理
deque
内部有个中控器
,维护每段缓冲区中的内容,缓冲区中存放真实数据
中控器
中存放每个缓冲区的地址,使得deque
时像一片连续的内存空间
3.deque构造函数
函数原型
deque<Y> deqT; //默认构造形式
deque(beg, end); //构造函数将 [beg,end) 区间中的元素拷贝给本身
deque(n, elem); //构造函数将n个连续的elem拷贝给本身
deque(const deque &deq); //拷贝构造函数
4.deque赋值操作
函数原型
deque& operator=(const deque &deq); //重载等号操作符
assign(beg, end); //将 [beg, end) 区间中的数据拷贝赋值给本身
assign(n, elem); //将n个elem拷贝赋值给本身
5.容器的大小
empty(); //判断容器是否为空
size(); //返回容器中元素的个数
//没有capacity();//没有容量限制
resize(int num);//重新制定容器的长度为num,若容器边长,则以默认值填充新位置、以0填充其他位置
//若容器变短,则末尾超出容器长度的元素被删除
resize(int num, elem);
void printx(const deque<int>&d)
{
for (deque<int>::const_iterator it = d.begin; it!=d.end(); it++)
{
cout << *it << endl;
}
}
...
deque<int> dd1 = {1,2,3,4,5,6};
printx(dd1);
...
6.deque插入和删除
两端的插入删除操作
push_back(elem); //在容器尾部添加一个数据
push_front(elem); //在容器头部插入一个数据
pop_back(); //删除容器最后一个数据
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位置的数据,返回下一个数据的位置
7.deque数据存取
at(int idx); //返回索引idx所指的数据
operator[]; //~
front(); //返回容器中第一个数据元素
back(); //返回容器中最后一个数据元素
8.deque排序
#include <algorithm>//标准算法头文件
sort(iterator beg, iterator end); //对 [beg, end) 区间内元素进行排序 、默认升序
五、stack-栈容器
1.stack基本概念
概念:stack
是一种先进后出
(First In Last Out,FILO)的数据结构,它只有一个出口
- 栈中只有顶端的元素可以被外界使用,因此栈不允许有遍历行为
2.栈常用接口
构造
stack<T> stk; //默认构造
stack<const stack &stk> //拷贝构造
赋值
stack& operator=(const stack &stk); //重载等号符
数据存取
push(elem); //栈入
pop(); //栈出
top(); //返回栈顶元素
大小操作
empty(); //判断栈是否为空
size(); //返回栈的大小
六、queue-队列容器
1.概念
先进先出
(First In First Out, FIFO)的数据结构,有两个出口
- 只有两端有数据访问口,不能有遍历的行为
2.队列容器常用接口
构造
queue<T> stk; //默认构造
queue<const queue &stk> //拷贝构造
赋值
queue& operator=(const queue &stk); //重载等号符
数据存取
push(elem); //队尾添加元素
pop(); //队头移除第一个元素
back(); //返回最后一个元素
front(); //返回第一个元素
大小操作
empty(); //判断队列是否为空
size(); //返回队列的大小
七、list-链表容器
链表
(list)是一种物理存储单元上非连续的储存结构,数据元素的逻辑顺序是通过链表中的指针链接实现的
组成
:链表是由一系列的节点
组成、节点由储存数据元素的数据域
,另一个是储存下一个节点地址的指针域
STL中的list链表容器是一个双向循环链表
- 可以快速的插入或删除元素,修改指针即可,不必移动大量元素
- 采用动态内存分配,不会造成内存浪费和溢出
- 遍历的速度没数组快
- 占用的空间比数组大
- list插入和删除操作都不会造成原有list迭代器的失效,这在vector是不成立的
- 由于链表的储存方式并不是连续的内存空间,因此链表list中的迭代器只支持前移和后移,不支持跳跃,属于
双向迭代器
1.list构造函数
list<T> list; //默认构造
list(beg, end); //将 [beg, end) 区间中的元素拷贝给本身
list(n,elem); //构造函数将n个elem拷贝给本身
list(const list &list); //拷贝构造函数
2.list赋值和交换
assign(beg, end); //将 [beg, end) 区间中的数据拷贝赋值给本身
assign(n, elem); //将n个elem拷贝赋值给本身
list& operator=(const list & lst); //重载等号符
swap(lst); //将lst与本身的元素互换
3.list大小操作
empty(); //判断容器是否为空
size(); //返回容器中元素的个数
resize(int num);//重新制定容器的长度为num,若容器边长,则以默认值填充新位置、以0填充其他位置
//若容器变短,则末尾超出容器长度的元素被删除
resize(int num, elem);
4.list插入和删除
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值匹配的元素,
使用list::remove()
删除自定义数据
核心就是第一个if的== 运算符,所以我们需要重载==运算符。不同编译器有不同特点,如果报错需要注意
- 在类外重载 == 运算符
- 用const修饰Person变量
class Person
{
public:
Person(string name, int age, int height)
{
this->m_Age = age;
this->m_Name = name;
this->m_height = height;
}
string m_Name;
int m_Age;
int m_height;
};
// 类外重载==让remove可以删除自定义的Person类型
bool operator==(const Person &p1, const Person &p2)
{
if(p1.m_Age == p2.m_Age && p1.m_height == p2.m_height && p1.m_Name == p2.m_Name)
return true;
return false;
}
void test03()
{
list<Person>l;
Person p1("张三", 10, 172);
Person p2("张三", 19, 145);
Person p3("张三", 15, 181);
Person p4("张三", 20,199);
Person p5("张三", 19, 171);
Person p6("张三", 19, 179);
l.push_back(p1);
l.push_back(p2);
l.push_back(p3);
l.push_back(p4);
l.push_back(p5);
l.push_back(p6);
l.remove(p3);
}
5.list数据存取
front(); //返回第一个元素
back(); //返回最后一个元素
6.反转和排序
reverse(); //反转列表
sort(); //链表排序
对自定义类型进行排序
#include <iostream>
#include <list>
#include <string>
#include <algorithm>
using namespace std;
class Person
{
public:
Person(string name, int age, int Height) :m_Name(name), m_Age(age), m_Height(Height) {};
string m_Name;
int m_Age;
int m_Height;//身高
};
bool comparePerson(Person &p1, Person &p2)
{
return p1.m_Age < p2.m_Age;
}
void myPrint(const Person & mp)
{
cout << mp.m_Name << mp.m_Age << mp.m_Height << endl;
}
void test()
{
list<Person> mlt;
Person p1("刘备",12, 21);
Person p2("曹操", 25, 180);
Person p3("张飞", 15, 170);
mlt.push_back(p1);
mlt.push_back(p2);
mlt.push_back(p3);
for_each(mlt.begin(),mlt.end(), myPrint);
cout << "-------------------------" << endl << "排序后" << endl;
mlt.sort(comparePerson);
for_each(mlt.begin(),mlt.end(), myPrint);
}
int main()
{
test();
system("pause");
}
八、set/multiset容器
- 所有元素都会在插入时被排序
- set/multiset属于
关联式容器
,底层结构是用二叉树
实现。
set
和multiset
区别
set
不允许容器中有重复的元素multiset
允许容器中有重复的元素
1.set构造和赋值
set<T> mset; //默认构造函数
set(const set &st); //拷贝构造函数
set& operator=(const set &set); //重载等号赋值
2.set大小和交换
size(); //返回容器中元素的数目
empty(); //判断容器是否为空
swap(st); //交换两个集合容器
3.插入和删除
insert(elem); //在容器中插入元素
clear(); //清除所有元素
erase(pos); //删除迭代器所指的元素,返回下一个元素的迭代器
erase(beg,end); //删除区间 [beg, end) 区间的所有元素,返回下一个元素的迭代器
erase(elem); //删除容器中值为elem的元素
4.查找和统计
find(key); //查找key是否存在,返回该键的元素的迭代器,若不存在,则返回set.end();
count(key); //统计key的个数、不是0就是1、而multiset可能会有多个数
5.set和mutiset的区别
- set不可以插入重复数据,而mutiset可以
- set插入数据的同时会返回插入结果,表示插入成功
- mutiset不会检测数据,因此可以插入重复数据
set<int> s;
pair<set<int>::iterator, bool> ret = s.insert(10);
if (ret.second)
{
//插入成功
}
6.pair对组创建
对组
:成对出现的数据,利用对组可以返回两个数据
pair<type, type> p (value1, vlaue2);
pair<type, type> p = make_pair(value1, vlaue2);
访问
cout << p.first << endl;
cout << p.second << endl;
7.set容器排序-利用仿函数(重载()
操作符)
set
容器默认排序为从小到大
class MyCompare
{
public:
bool operator()(int v1, int v2)
{
return v1 > v2;
}
};
//指定排序规则为从大到小
set<int, MyCompare> s1;
s1.insert(20);
s1.insert(130);
s1.insert(120);
s1.insert(150);
s1.insert(50);
s1.insert(20);
8.set存放自定义数据、如何排序
#include <iostream>
#include <set>
#include <string>
#include <algorithm>
using namespace std;
class Person
{
public:
Person(string name, int age, int Height) :m_Name(name), m_Age(age), m_Height(Height) {};
string m_Name;
int m_Age;
int m_Height;//身高
};
class comparePerson
{
public:
bool operator()(const Person &p1, const Person &p2)
{
return p1.m_Age < p2.m_Age;
}
};
void myPrint(const Person & mp)
{
cout << mp.m_Name << mp.m_Age << mp.m_Height << endl;
}
void test()
{
set<Person, comparePerson> mlt;
Person p1("刘备",12, 21);
Person p2("曹操", 25, 180);
Person p3("张飞", 15, 170);
mlt.insert(p1);
mlt.insert(p2);
mlt.insert(p3);
for_each(mlt.begin(),mlt.end(), myPrint);
}
int main()
{
test();
system("pause");
}
九、map/mutimap容器
简介:
map
所有元素都是pair
pair
中第一个元素为key
(键值),起到索引作用,第二个元素为value
(实值)- 所有元素都会根据元素的键值
自动排序
本质:
map/mutimap
属于关联式容器
,底层结构是用二叉树
实现的。
优点:
- 可以根据key值快速找到vlaue值
map
与mutimap
的区别
map
不允许容器有重复key值mutimap
允许容器有重复的key值
1.map构造和赋值
map<T key,T value> mp; //map默认构造函数
map(const map &map); //拷贝构造函数
map& operator=(const map &map); //等号操作符重载
范例
#include <map>
map<int, int> mp;
mp.insert(pair<int, int> (1,10));
2.map大小和交换
size(); //返回容器中元素的数目
empty(); //判断容器是否为空
swap(mp); //交换两个集合容器
3.map插入和删除
insert(elem); //在容器中插入元素
clear(); //清除所有元素
erase(pos); //删除迭代器所指的元素,返回下一个元素的迭代器
erase(beg,end); //删除区间 [beg, end) 区间的所有元素,返回下一个元素的迭代器
erase(key); //删除容器中key值对应的元素
#include <map>
map<int, int> mp;
mp.insert(pair<int, int> (1,10));
mp.insert(make_pair(2,2)); //建议
mp.insert(map<int, int>::value_type(3, 30));
mp[4] = 40; //不建议用来插入、因为如果不存在mp[i], 系统会自动创建一个新的,并将value复制为0
4.map查找和统计
find(key); //查找key是否存在,如果存在返回该键的迭代器,若不存在则返回map.end()
count(key); //统计key的元素个数
5.map容器排序-利用仿函数
#include <iostream>
#include <map>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
class Labours
{
public:
Labours(string name, int Salary) :m_name(name), m_Salary(Salary){};
string m_name;
int m_Salary;
};
void createLab(vector<Labours> &labs)
{
labs.push_back(Labours("顾笑甜",10000));
labs.push_back(Labours("邢林", 556565));
labs.push_back(Labours("liu", 568462));
}
void myPrint(const pair<int, Labours> &pa)
{
cout << pa.first << pa.second.m_name << pa.second.m_Salary << endl;
}
void setGroup(vector<Labours> & mvlabs, multimap<int, Labours> & mmaplabs)
{
for (vector<Labours>::iterator it = mvlabs.begin(); it!= mvlabs.end(); it++)
{
int depId = rand() % 3;
mmaplabs.insert(make_pair(depId, *it));//make_pair创建一个对组,并放入这两个数据
}
}
vector<Labours> mlabs;
multimap<int, Labours> mLmap;
int main()
{
createLab(mlabs);
setGroup(mlabs, mLmap);
cout << "mlabs size =" << mlabs.size() << endl;
cout << "mLmap size =" << mLmap.size() << endl;
for_each(mLmap.begin(), mLmap.end(), myPrint);
system("pause");
}