2 STL初识
2.1 STL的诞生
- 长久以来,软件界一直希望建立一种可重复利用的东西
- C++的面向对象和泛型编程思想,目的就是复用性的提升
- 大多情况下,数据结构和算法都未能有一套标准导致被迫从事大量重复工作。为了建立数据结构和算法的一套标准,诞生了STL
2.2 STL基本概念
- STL(**Standard Template Library,**标准模板库)
- STL从广义上分为:容器(container) 算法(algorithm) 迭代器(iterator)。容器和算法之间通过迭代器进行无缝连接。
- STL几平所有的代码都采用了模板类或者模板函数
2.3 STL六大组件
STL大体分为六大组件,分别是:
容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器
- 容器: 各种数据结构,如vector、list、deque、set、map等,用来存放数据
- 算法: 各种常用的算法,如sort、find、copy、for_each等
- 选代器: 扮演了容器与算法之间的胶合剂。(容器与算法之间沟通的桥梁)
- 仿函数:行为类似函数,可作为算法的某种策略
- 适配器:一种用来修饰容器或者仿函数或迭代器接口的东西。
- 空间配置器:负责空间的配置与管理
2.4 STL中容器、算法、迭代器
容器:置物之所也
STL容器就是将运用最广泛的一些数据结构实现出来
常用的数据结构:数组,链表,树,栈队列集合 映射表等这些容器分为序列式容器和关联式容器两种:
- 序列式容器:强调值的排序,序列式容器中的每个元素均有固定的位置(输入什么就是什么)
- 关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系(可能根据容器的特性对输入的数据自动排序)
算法:问题之解法也
解决问题的有限步骤,解决逻辑或数学上的问题,这一门学科我们叫做算法(Algorithms)算法分为:质变算法和非质变算法
- 质变算法:是指运算过程中会更改区间内的元素的内容。例如拷贝,替换,删除等等(原来存储的内容可能会改变)
- 非质变算法: 是指运算过程中不会更改区间内的元素内容,例如查找、计数、遍历、寻找极值等等(原来存储的内容不会被改变)
迭代器:容器和算法之间粘合剂
提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式
每个容器都有自己专属的迭代器
迭代器使用非常类似于指针,初学阶段我们可以先理解迭代器为指针
2.5 容器算法迭代器初识
了解STL中容器、算法、迭代器概念之后,我们利用代码感受STL的魅力STL中最常用的容器为Vector,可以理解为数组,下面我们将学习如何向这个容器中插入数据、并遍历这个容器
2.5.1 vector存放内置数据类型
//方法1、直接遍历
vector::iterator itBegin = v.begin();
vector::iterator itEnd = v.end();
while (itBegin != itEnd)
{
cout << *itBegin << endl;
itBegin++;
}
//方法2:for循环
for (vector::iterator it = v.begin(); it < v.end(); it++)
{
cout<<“:::” << *it << endl;
}
//方法3:for_each
//for_each(_InIt _First, _InIt _Last, _Fn _Func),
//底层实际上是一个for循环的形式
for_each(v.begin(), v.end(), Print);
// ste.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include "pch.h"
#include <iostream>
#include<vector>
#include<algorithm>//标准算法的头文件
using namespace std;
void Print(int val)
{
cout << val << " " << endl;
}
void test()
{
vector<int> v;
for (int i = 0; i < 5; i++)
{
v.push_back(i);
}
//方法1、直接遍历
vector<int>::iterator itBegin = v.begin();
vector<int>::iterator itEnd = v.end();
while (itBegin != itEnd)
{
cout << *itBegin << endl;
itBegin++;
}
//方法2:for循环
for (vector<int>::iterator it = v.begin(); it < v.end(); it++)
{
cout<<":::" << *it << endl;
}
//方法3:for_each
//for_each(_InIt _First, _InIt _Last, _Fn _Func),
//底层实际上是一个for循环的形式
for_each(v.begin(), v.end(), Print);
}
int main()
{
test();
std::cout << "Hello World!\n";
}
2.5.2 Vector存放自定义数据类型
学习目标:
vector中存放自定义数据类型,并打印输出
// ste.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include "pch.h"
#include <iostream>
#include<vector>
#include<algorithm>//标准算法的头文件
#include<string>
using namespace std;
class Person
{
public:
Person(string name, int age)
{
this->m_name = name;
this->m_age = age;
}
string m_name;
int m_age;
};
void printPerson(Person *p1)
{
cout << p1->m_name<<" - "<< p1->m_age << endl;
}
void test02()
{
cout << "test02()" << endl;
vector<Person> v1;
Person p1("Dasd", 12);
Person p2("Daewqsd", 13);
Person p3("Ddsaasd", 14);
Person p4("Dacfdsd", 15);
v1.push_back(p1);
v1.push_back(p2);
v1.push_back(p3);
v1.push_back(p4);
//for_each(v1.begin(), v1.end(), printPerson);
}
//存放自定义类型的指针
void test03()
{
cout << "test03()" << endl;
vector<Person*> v1;
Person p1("Dasd", 12);
Person p2("Daewqsd", 13);
Person p3("Ddsaasd", 14);
Person p4("Dacfdsd", 15);
v1.push_back(&p1);
v1.push_back(&p2);
v1.push_back(&p3);
v1.push_back(&p4);
for_each(v1.begin(), v1.end(), printPerson);
for (vector<Person*>::iterator it = v1.begin(); it < v1.end(); it++)
{
cout << (*it)->m_name << " - - - " << (*it)->m_age << endl;
}
}
int main()
{
test();
//test02();
test03();
std::cout << "Hello World!\n";
}
2.5.3 Vector容器嵌套容器
容器嵌套容器,并遍历输出
void test04()
{
vector<vector<int>> v;
vector<int > v1;
vector<int > v2;
vector<int > v3;
vector<int > v4;
for (int i = 0; i < 6; i++)
{
v1.push_back(i);
v2.push_back(i+1);
v3.push_back(i + 2);
v4.push_back(i + 3);
}
v.push_back(v1);
v.push_back(v2);
v.push_back(v3);
v.push_back(v4);
for (vector<vector<int>>::iterator it = v.begin(); it < v.end(); it++)
{
for (vector<int>::iterator it1 = (*it).begin(); it1 < (*it).end(); it1++)
{
cout << *it1 << " ";
}
cout << endl;
}
}
3 STL常用容器
void test05()
{
string s1;
s1 = "fsfsdfs";
const char *str = "hello world";
string s2(str);
string s3("fdsfs");
string s4(s2);//拷贝构造函数
string s5(5, 'a');
cout << s1 << endl;
cout << s2 << endl;
cout << s3 << endl;
cout << s4 << endl;
cout << s5 << endl;
}
string的赋值方式
s5 = s4;
cout <<"s5:"<< s5 << endl;
string s6;
s6.assign("dasdas");
cout <<"s6:"<< s6 << endl;
string s7;
s7.assign("dasdas",3);
cout << "s7:" << s7 << endl;
string s8;
s8.assign(s7);
cout << "s8:" << s8 << endl;
string s9;
s9.assign(9,'a');
cout << "s9:" << s9 << endl;
string的拼接方式
string a;
string b;
a = "我";
b = " xihuan money!";
a += b;
a.append("ds");
cout << a << endl;
string的查找和替换
- find是从左往右查找
- rfind是从右至左查找
- 但是返回的位置依然是从左往右数
string ds = "dasdasd";
cout<<ds.find("sd")<<endl;//find是从左往右查找
cout<<ds.rfind("sd")<<endl;//rfind是从右至左查找
cout << ds.replace(3,2,"111") << endl;
字符串比较
string str1="hello";
string str2="xello";
if(str1.compare(str2)==0)
{
cout<<"str1==str2"<<endl;
}
string字符存取
for(int i=0;i<ds.size();i++)
{
cout << ds[i] << endl;
cout << ds.at(i) << endl;
}
ds[2]='f';
ds.at(2)='s';
string插入和删除
str.insert(1,"fdsfd");
str.erase(1,3);//从1位置起,删除3各字符
string字串
从字符串中获取想要的子串
str.substr(1,3);//返回从1位置起由3个字符组成的字串
3.2vector容器(动态数组)
vector容器的迭代器是支持随机访问的迭代器
注意:vector的可取区间是前闭后开的。
void PrintVector(vector<int>&v)
{
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " " ;
}
cout << endl;
}
void test000()
{
vector<int>v1;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
PrintVector(v1);
//通过区间的方式构造vector
vector<int>v2(v1.begin(), v1.end());
PrintVector(v2);
//通过n个element方式构造vector
vector<int>v3(10,100);
PrintVector(v3);
//通过拷贝构造函数方式构造vector
vector<int>v4(v3);
PrintVector(v4);
}
3.2.3 vector赋值操作
功能描述:
给vector容器进行赋值
函数原型:
vector& operator=(const vector &vec); //重载等号操作符
assign(beg, end);
assign(n, elem);
//将[beg,end)区间中的数据拷贝赋值给本身//将n个elem拷贝赋值给本身。
//赋值操作
vector<int>v5;
v5.assign(v1.begin(), v1.end());
PrintVector(v5);
vector<int>v6;
v6=v4;
PrintVector(v6);
vector<int>v7;
v7.assign(6, 6);
PrintVector(v7);
注意:capacity总是大于等于size
cout << v7.empty() << "--" << endl;
cout << v7.capacity() << endl;
cout <<"size():"<< v7.size() << endl;
v7.resize(10);
v7.resize(12, 1);
PrintVector(v7);
3.2.5 vector插入和删除
功能描述:
- 对vector容器进行插入、删除操作
函数原型:
push_back(ele);
pop_back();
insert(const iterator pos, ele);
//尾部插入元素ele
//删除最后一个元素
//迭代器指向位置pos插入元素ele
insert(const_iterator pos,int count,ele); //选代器指向位置pos插入count个元素ele
erase(const_iterator pos);
//删除迭代器指向的元素
erase(const_iterator start, const_iterator end); //删除选代器从start到end之间的元素
clear();
//删除容器中所有元素
尾插法–push_back(ele);
尾删法–pop_back();
头插法
v7.insert(v7.begin(), 13);
PrintVector(v7);
v7.insert(v7.begin(), 5, 21);
PrintVector(v7);
//删除头部第一个元素
v7.erase(v7.begin());
PrintVector(v7);
//清空
//v7.erase(v7.begin(),v7.end());
v7.clear();
PrintVector(v7);
//获取第一个元素
cout<<v7.front();
//获取最后一个元素
cout << v7.back();
3.2.7 vector互换容器
功能描述:
实现两个容器内元素进行互换
函数原型:
// 将vec与本身的元素互换swap(vec);
注意会使用swap收缩内存
vector<int>vs1;
for (int j = 0; j < 10; j++)
{
vs1.push_back(j);
}
PrintVector(vs1);
vector<int>vs2;
for (int j = 10; j >0 ; j--)
{
vs2.push_back(j);
}
PrintVector(vs2);
vs1.swap(vs2);
PrintVector(vs1);
PrintVector(vs2);
vs2.resize(2);
//(当原本开放的vector容量较大,在resize之后,可能vector只使用了前几个,导致后边的内存浪费)
//使用swap收缩内存,减少内存空间的浪费
vector<int>(v2).swap(v2);
PrintVector(vs2);
3.2.8 vector预留空间
功能描述:
- 减少vector在动态扩展容量时的扩展次数
函数原型:
reserve(int len); //容器预留len个元素长度,预留位置不初始化,元素不可访问。
如果在使用vector之前,已经知道需要的空间很大,那么,使用reserve可以减少动态扩展的次数,提升程序运行的效率。
3.3 deque容器(双端队列)
3.3.1 deque容器基本概念
功能:
- 双端数组,可以对头端进行插入删除操作
deque与vector区别:
- vector对于头部的插入删除效率低,]数据量越大,效率越低。
- deque相对而言,对头部的插入删除速度回比vector快。
- vector访问元素时的速度会比deque快,这和两者内部实现有关
- deque容器的迭代器也是支持随机访问的
deque内部工作原理:
deque内部有个中控器,维护每段缓冲区中的内容,缓冲区中存放真实数据中控器维护的是每个缓冲区的地址,使得使用deque时像一片连续的内存空间
void PrintDeque(const deque<int> &de)
{
for (deque<int>::const_iterator it = de.begin(); it != de.end(); it++)
{
//使用const_iterator之后,使得迭代的数值不能修改
cout << *it << " ";
}
cout << endl;
}
void test001()
{
deque<int> de;
for (int i = 0; i < 10; i++)
{
//尾插
//de.push_back(i);
//头插
de.push_front(i);
}
PrintDeque(de);
//尾出
de.pop_back();
PrintDeque(de);
//头出
de.pop_front();
PrintDeque(de);
//区间的方式构造
deque<int> de1(de.begin(),de.end());
PrintDeque(de1);
//n个数据的方式构造
deque<int> de2(10,5);
PrintDeque(de2);
//拷贝构造
deque<int> de3=de;
PrintDeque(de3);
}
注意:deque没有容量的限制,因此没有capacity
3.3.7 deque 排序功能描述:
- 利用算法实现对deque容器进行排序
算法:
sort(iterator beg,iterator end) //对beg和end区间内元素进行排序
void test002()
{
deque<int> dek;
dek.push_back(654);
dek.push_back(32);
dek.push_back(64);
dek.push_back(12);
dek.push_back(743);
sort(dek.begin(), dek.end());//默认升序排列
PrintDeque(dek);
}
评委打分
void sta()
{
stack<int> st;
st.push(10);
st.push(20);
st.push(30);
st.push(40);
st.push(50);
cout << st.size() << endl;
while (!st.empty())
{
cout << st.top() << endl;
st.pop();
}
cout << st.size() << endl;
}
void PrintList(list<int> &h)
{
for (list<int>::const_iterator it = h.begin(); it !=h.end(); it++)
{
cout << (*it) << " ";
}
}
//链表的操作
void lis()
{
list<int> Head;
Head.push_back(10);
Head.push_back(20);
Head.push_back(30);
Head.push_back(40);
PrintList(Head);
//区间构造
list<int> L(Head.begin(),Head.end());
PrintList(L);
//拷贝构造
list<int> L2(L);
PrintList(L2);
//n个element
list<int> L1(15,1);
PrintList(L1);
}
//使用迭代器进行插入操作
list<int>::iterator it = L2.begin();
L2.insert(++it, 100);
PrintList(L2);
//使用迭代器进行删除操作
it = L2.begin();
L2.erase(it);
PrintList(L2);
//移除操作
L2.remove(20);
PrintList(L2);
注意:
- list容器不支持随机访问,即不可以使用[ ]或at的方式进行访问
- list的迭代器也是不支持随机访问的,不可以通过it+=3的方式移动迭代器,只支持it++等
bool Compare(int a,int b)
{
return a > b;
}
reverse(L2.begin(), L2.end());
PrintList(L2);
//排序
L2.sort();//默认升序排列
PrintList(L2);
//降序排列
L2.sort(Compare);
PrintList(L2);
案例
void testt()
{
set<int> s;
s.insert(10);
s.insert(30);
s.insert(20);
s.insert(10);
s.insert(5);
for (set<int>::iterator it = s.begin(); it != s.end(); it++)
{
cout << (*it) << "s "<< endl;
}
set<int> s2 = s;
for (set<int>::iterator it = s2.begin(); it != s2.end(); it++)
{
cout << (*it) << "s2 " << endl;
}
}
multiset可以插入多个重复数据
multiset<int> ms;
ms.insert(101);
ms.insert(101);
ms.insert(101);
ms.insert(101);
for (multiset<int>::iterator it = ms.begin(); it != ms.end(); it++)
{
cout << (*it) << "-ms " << endl;
}
void gh()
{
pair<string, int>p("Tom", 15);
cout << p.first << " " << p.second << endl;
pair<string, int>p1 = make_pair("Jerry", 19);
cout << p1.first << " " << p1.second << endl;
}
//set容器一般在插入之前就应当确定排序顺序,默认排序为“升序”
//如果要设置降序排列,那么需要使用仿函数
普通数据类型排序
class compar
{
public:
bool operator()(int v1,int v2)
{
return v1 > v2;
}
};
void fanghanshu()
{
set<int> s;
s.insert(100);
s.insert(200);
s.insert(300);
s.insert(400);
s.insert(500);
for (set<int>::iterator it = s.begin(); it != s.end(); it++)
{
cout << (*it) << "-s " << endl;
}
//set容器一般在插入之前就应当确定排序顺序,默认排序为“升序”
//如果要设置降序排列,那么需要使用仿函数
set<int,compar> s1;
s1.insert(100);
s1.insert(200);
s1.insert(300);
s1.insert(400);
s1.insert(500);
for (set<int,compar>::iterator it = s1.begin(); it != s1.end(); it++)
{
cout << (*it) << "-s1 " << endl;
}
}
自定义数据类型排序
class compd
{
public:
bool operator()(const Student&s1, const Student&s2 )
{
return s1.m_age > s2.m_age;
}
};
//set对自定义数据类型排序
void paixu()
{
set<Student,compd> stu;
Student stu1("dwas", 35, 175);
Student stu2("das", 45, 180);
Student stu3("daas", 40, 170);
Student stu4("ddsas", 25, 190);
Student stu5("dadss", 35, 160);
Student stu6("dcsas", 35, 200);
stu.insert(stu1);
stu.insert(stu2);
stu.insert(stu3);
stu.insert(stu4);
stu.insert(stu5);
stu.insert(stu6);
for (set<Student, compd>::iterator it = stu.begin(); it != stu.end(); it++)
{
cout << (*it).m_name << "321::123::" << (*it).m_age << endl;
}
}
void mapV()
{
//map默认按照key进行排序
map<int, int> m;
m.insert(pair<int,int>(1,10));
m.insert(pair<int, int>(2, 20));
m.insert(pair<int, int>(3, 30));
m.insert(pair<int, int>(4, 40));
m.insert(pair<int, int>(5, 50));
for (map<int,int>::iterator it = m.begin(); it != m.end(); it++)
{
cout << (*it).first<< " " << (*it).second << endl;
}
//拷贝构造函数
map<int, int> m1(m);
for (map<int, int>::iterator it = m1.begin(); it != m1.end(); it++)
{
cout << (*it).first << " " << (*it).second << endl;
}
//赋值操作
map<int, int> m2;
m2 = m;
for (map<int, int>::iterator it = m2.begin(); it != m2.end(); it++)
{
cout << (*it).first << " " << (*it).second << endl;
}
}
//交换两个map
map<int, int> m3;
m3.insert(pair<int, int>(30, 30));
m3.insert(pair<int, int>(40, 40));
m3.insert(pair<int, int>(50, 50));
m3.swap(m2);
for (map<int, int>::iterator it = m3.begin(); it != m3.end(); it++)
{
cout << (*it).first << " " << (*it).second << endl;
}
//自定义“降序”排列,需要指定仿函数
map<int, int, compar> m;
m.insert(pair<int,int>(1,10));
m.insert(pair<int, int>(2, 20));
m.insert(pair<int, int>(3, 30));
m.insert(pair<int, int>(4, 40));
m.insert(pair<int, int>(5, 50));
for (map<int,int, compar>::iterator it = m.begin(); it != m.end(); it++)
{
cout << (*it).first<< " " << (*it).second << endl;
}
4 STL- 函数对象
4.1函数对象
4.1.1函数对象概念
概念:
- 重载函数调用操作符的类,其对象常称为函数对象
- 函数对象使用重载的()时,行为类似函数调用,也叫仿函数本质:
- 函数对象(仿函数)是一个类,不是一个函数
4.1.2函数对象使用
特点:
- 函数对象在使用时,可以像普通函数那样调用,可以有参数,可以有返回值
- 函数对象超出普通函数的概念,函数对象可以有自己的状态
- 函数对象可以作为参数传递
4.2 谓词
4.2.1 谓词概念
概念:
- 返回bool类型的仿函数称为谓词
- 如果operator()接受一个参数,那么叫做一元谓词
class GreaterFive
{
bool operator()(int val)
{
return val > 5;
}
};
void TA()
{
vector<int> v;
for (int i = 0; i < 6; i++)
{
v.push_back(i);
}
//查找容器中是否有大于5的数字
vector<int>::iterator pos = find_if(v.begin(), v.end(), GreaterFive());
if (pos == v.end())
{
cout << "未找到!" << endl;
}
else
{
cout << "找到了!" << endl;
}
}
- 如果operator()接受两个参数,那么叫做二元谓词
class GreaterFive
{
bool operator()(int val1,int val2)
{
return val1> val2;
}
};
4.3 内建函数对象
4.3.1 内建函数对象意义概念:
- STL内建了一些函数对象分类:
- 算术仿函数
- 关系仿函数
- 逻辑仿函数
用法:
这些仿函数所产生的对象,用法和一般函数完全相同使用内建函数对象,需要引入头文件 #include<functiona1>
negate<int>n;
cout<< n(50) << endl;
plus<int>p;
p(10,20);
transform
find
find_if
adjacent_find
binary_search
bu
注意:binary_search()算法需要查找的是一个有序序列,如果序列无序,那么查询结果可能错误
count
count_if
random_shuffle
注意:random_shuffle是基于系统的随机数排序的
merge
reverse
replace
copy
replace_if
swap
注意:swap需要保持两个容器相同才能互换
set_accumulate
fill
set_intersection
set_union
set_difference