目录 |
STL的基本概念 |
STL的基本组件 |
vector容器 |
string容器 |
list容器 |
stack容器 |
deque容器 |
set/multiset容器 |
queue容器 |
map/multimap容器 |
仿函数 |
谓词 |
算术仿函数 |
关系仿函数 |
逻辑仿函数 |
遍历算法 |
查找算法 |
算术生成算法 |
排序算法 |
拷贝和替换算法 |
1.STL从广义上可以分为容器,算法,迭代器
2.容器和算法之间通过迭代器进行无缝连接
3.STL几乎所有的代码都采用了模板类型或者模板函数
1.STL的六大组件包括容器 算法 迭代器 仿函数(重载的小括号也就是行为类似函数) 配置器(修饰容器或者仿函数或者迭代器接口的东西) 空间配置器(负责空间的配置和管理)
2.容器:放数据。运用最广泛的数据结构实现。
容器分为序列式容器和关联式容器。
序列式容器强调的是值的排序,序列式容器:强调的是值的排序,序列式容器中的每个元素都有固定的位置。
关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系。
3.算法:问题之解法也
质变算法:运算过程中更改区间中元素的内容,例如拷贝,替换,删除等等
非质变算法:运算过程中不会更改区间中元素的内容,例如查找,遍历,计数,寻找极值
4.迭代器:容器和算法之间的粘合剂
提供一种方法,使之能够依序寻找某个容器所含的各个元素,而又无需暴露容器的内部联系方式。每个容器都有自己的迭代器。
迭代器使用非常类似于指针。
常用的迭代器类型为双向迭代器(读写操作,并且能够向前向后操作)和随机访问迭代器(读写操作,并且可以以任意的方式去访问任意数据,功能最强)
1)vector容器可以理解为数组,单端数组。
但是不同之处在于数组是静态空间,但是vector是可以动态扩展。动态扩展并不是在原有的空间之后续空间,而是把原有的数据拷贝到新空间里面,释放原有的空间。
vector容器的迭代器是支持随机访问的迭代器
容器:vector
算法:for_each
迭代器:vector::iterater
void myprint(int val)
{
cout<
#include
#include//标准算法的头文件
void test01()
{
//创建了一个vector容器,认为它是一个数组
vectorv;
//向容器中插入数据
v.push_back(10);
//通过迭代器访问容器中的数据
vector::iterator itBegin=v.bagin();//起始迭代器 指向容器中的第一个元素
vector::iterator itEND=v.end();//结束迭代器 指向容器中的最后一个元素的下一个位置
//第一种遍历方式:
while(itBegin!=itEnd)
{
cout<<*itBegin<::iterator it=v.begin();it!=v.end;it++)
{
cout<<*it<里面是什么数据类型,*it解出来的也是相同的数据类型
}
//第三种遍历
for_each(v.begin(),v.end(),myPrint);
}
自定义数据类型通过容器访问内部成员
(*it).m_name;
it->m_name;
2)vector容器中嵌套一个容器(二维数组)
void test01()
{
vector>v;
//创建小容器
vectorv1;
vectorv2;
vectorv3;
vectorv4;
//向小容器添加数据
for(int i=0;i<4;i++)
{
v1.push_back(i=1);
v2.push_back(i=2);
v3.push_back(i=3);
v4.push_back(i=4);
}
//将小容器插到大容器中去
v.push_back(v1);
v.push_back(v2);
v.push_back(v3);
v.push_back(v4);
//通过大容器把所有数据遍历一遍
for(vector>::iterator it=v.begin();it!=v.end();it++)
{
//(*it)--容器vector
for(vector::iterator vit=(*it).begin();vit!=(*it).end;vit++)
{
cout<<*vit<
3)vector函数的默认构造
vector v;//利用模板实现类实现,默认构造函数
vector//利用v[bagin(),end()]区间的元素拷贝到自身
vector;//构造函数把n个elem拷贝给自身
vector
4)vector的赋值操作
vector v2;
v2=v1;//
vector v3;
v3.assign(v1.begin(),v1.end());//
vector v4;
v4.assign(10,100);//n个elem方式赋值
5)vector的插入和删除
v1.push_back(1);//在尾部插入一个1
v1.pop_back();//尾删
v1.insert(v1.begin(),100);//在头部插入100 第一个参数是迭代器
v1.insert(v1.begin(),2,1000);//在头部插入2个1000
v1.erase(v1.begin());//头删的参数也是迭代器
v1.erase(v1.begin,v2.end);//清空 只剩换行
v1.clear;//同清空
6)vector的数据存取
v[i];//利用[]来访问
v.at(i);//利用at来访问
v1.front;//获取第一个位置上的元素
v1.end;//获取最后一个位置的元素
7)vector容器的互换器
v1.swap(v2);
//用swap可以收缩空间大小
vector(v).swap(v);//匿名对象.容器交换
8)vector预留空间
减少vector在动态扩展容量时的扩展次数
reserve(int len);//容器预留len个元素长度,预留位置不初始化,元素不可访问
9)vector容器的容量和大小
empty();//判断容器是否为空
capacity();//容器的容量
size();//返回容器的元素个数
resize(int num);//重新指定容器的长度为num,如果容器变长,则以默认值填充新位置
//如果容器变短,则末尾超出容器长度的元素被删除
resize(int num,elem);//同上只不过靠elem填充新位置
1.string和char*的区别:char*是一个指针.string是一个类,类内封装了char* ,管理这个字符串,是一个char*类型的容器。
2.特点:封装了很多成员方法:find copy replace insert
3.string的构造方式:
string s1;//默认构造
const char*str="hello world";
string s2(str);//使用字符串初始化
string(const string& str);//使用一个string对象初始另一个string对象
string s3(s2);
string(int n,char c);//使用n个字符初始化
4.string的赋值操作:
void test()
{
string str1;
str1="hello";
string str2;
str2=str1;
string str3;
str3='a';
string str4;
str5.assign("hello");
string str5;
str5.assign("hello",3);//把字符串的前3个字符赋值给str5
string str6;
str6.assign(str5);
string str7;
str7.assign(10,'*');//用10个*
}
5.字符串的拼接:
//void test01()
{
string str1="我";
str1+="爱你";//追加
str1+=':';
str2="t";
str1+=str2;
string str3="I";
str3.append("love");
str3.append("game abbcd",4);//把前四个拼接进去
str3.append(arr2);
}
void test()
{
string str1="我“;
string str2="yyds sdbz";
str1.append(str2,0,4);//从str的第零个位置截取4个字符
str1.append(str2,4,4);//从str的第四个位置开始截取四个字符
}
6)string的查找(指定的字符串是否存在)和替换(在指定的位置替换字符串)
void test01()
{
string str1="abcdef";
int pos=str1.find("de");//输出3 地址从0开始数 地址不存在的时候会返回-1
int pos2=str1.rfind;//也输出3
//rfind从右往左查找,find从左往右查找
}
void test02()
{
//从第n个位置起的几个字符替换成什么样的字符串
string str1="abcdefg";
str1.replace(1,3,"1111");//从第一个位置开始数三个字符替换成1111的字符串a1111efg
}
7)string字符串的比较:比得是字符的ASCII码值逐个对比
相等返回0 第一个大的话返回1 第一个小的话返回-1
if(str1.compare(str2)==0)
{
cout<<"str1==str2"<
8)string的字符存取
string的单个字符的存取方式有俩种
//通过[]来访问单个字符
for(int i=0;i
//修改单个字符
str[0]='a';
str.at(1)='a';
9)string中的插入和删除
//插入
str.insert(1,'111');//在第一个位置插入三个1
//删除
str .erase(1,3)//从第一个位置删三个
10)string子串——常用接口(从字符串中截取想要的子串)
string str1='abcdef';
string str2=str.substr(1,3);//从第一个的位置截取三个bcd
//实用操作 从邮件地址中获取用户名信息
void test02()
{
string email="[email protected]";
int pos=email.find("@");
string userName=email.substr(0,pos);
}
deque容器:双端数组,可以对头端进行插入和删除操作。
和vector的区别:vector对于头部的插入删除效率低,数据量越大,效率越低
deque相对而言,对头部的插入删除速度比vector快
vector访问元素的速度比deque快,这和俩者的内部实现有关。
//vector的赋值操作
1)等号赋值
v2=v1;
2)assign赋值
v3.assign(d1.begin(),d1.end());
3)
v4=assign(10,100);
//deque的大小操作
d1.empty();//空为1非空为0
d1.size();//容器中的元素个数
deque容器没有容量的概念
d1.resize(15,1);//重新指定大小
//deque的删除和插入
//尾插
d1.push_back(10);
//头插
d1.push_front(10);
//尾删
d1.pop_back();
//尾删
d1.pop_front();
//d1.insert(d1.begin(),1000);
d1.insert(d1.begin(),2,10000);//在头部插入俩个一万
//按照区间来进行插入
d1.insert(d1.begin(),d2.begin(),d2.end());//在d1的头部插入d2
//删除
d1.erase();
//按照区间来删除
d1.erase(d1.begin(),d1.end());
//清空
d1.clear;
//deque的数据存储
[]访问
d[i]
at访问
d.at(i);
d.front
d.end
//deque的排序
算法:sort(d.begin().d.end());//默认从小到大升序排列
对于支持随机访问的迭代器容器,都可以利用sort算法直接对其进行排序。vector也可以用这个方式进行排序。
一种先进后出的数据结构,通常只有一个出口。栈不允许有遍历行为,会有元素的改动。栈可以判断容器是否为空,栈可以返回它的元素个数(入栈的时候记录个数)
#include
#include
using namespace std;
void test01()
{
stacks;//默认构造
//入栈
s.push(10);
s.push(20);
//只要栈不为空,查看栈顶,最后执行出栈
while(!s.empty())
{
cout<<"栈顶元素"<
queue队列容器:符合先进先出的数据结构,它有俩个出口。队尾只能进数据,对头只能出数据。
进叫做入队(push),出叫做出队(pop)。
1)队列容器允许从一段新增元素,从另一端移除元素
2)队列中只有头和尾才能被外部访问,因此队列中不允许有遍历行为
3)队列不允许遍历
#include
using namespace std;
#include
class person
{
public:
person(string name,int age)
{
this->m_name=name;
this->m_age=age;
}
int m_age;
int m_name;
}
void test()
{
queue q;
person p1("tt",19);
q.push(p1);
//只要队列不为空,查看队头和队尾,出队
while(!q.empty())
{
cout<
将数据进行链式存储
链表是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针实现的。链表是由结点组成的。每个结点是由指针域和数据域组成的。
优点:可以对任意的位置进行快速插入或者删除元素
缺点:对容器中元素的遍历速度较慢,占用的空间大于数组
STL中的链表是一种双重循环链表:每一个结点都记录着前面和后面的指针域,最后一个结点指向第一个结点,第一个指向最后一个。
链表的存储方式并不是连续的内存空间,因此链表list中的迭代器只支持前移和后移,属于双向迭代器。
//list构造函数
listint;
list(begin,end);
list(n,elem);
listl2(l1.begin(),l1.end());//区间构造
list(const list &lst);//拷贝构造
listl3(l2);
//list的赋值和交换
#include
#include
void test01()
{
listL1;
//插入
L1.push(10);
L1.push(20);
listL2;
L2=L1;//赋值
L2.assign(L2.begin(),L2.end());
L2.assign(10,100);
//交换
L1.swap(L2);
};
//list容器中的大小操作
size()
emptu()
resize(n,elem);
resize(n);
//List的插入和删除
//在pos位置插入elem元素
insert(pos.elem);
//在pos位置插入n个elem
insert(pos,n,elem);
//移除容器中的所有数据
clear();
//删掉(begin,end)区间的数据
erase(begin,end);
//删除pos位置的数据,返回下一个数据
erase(pos);
//删除容器中和elem匹配的元素
remove(elem);
list数据的存储
front();//返回第一个元素
back();//返回最后一个元素
//验证迭代器是不支持随机访问的 支持双向的
list::iterator it=li.begin();
it--;
it++;//但是it=it+1 +2……会报错
//list的反转和排序
//把容器中的元素反转,以及对容器中的数据进行排序
reverse();
sort();
void print(const list&L)
{
for(list::const_iterator it=L.begin;it!=L.end;it++)
{
cout<<"*it"<l1;
l1.push_back(10);
l1.push_back(20);
l1.reverse();
sort(l1.begin(),l2.end);//err 所有不支持随机访问迭代器的容器,不支持标准算法
//不支持随机访问迭代器的容器内部会提供一些对应的算法
l1.sort();//默认从小到大
}
//从大到小排序
bool myCompare(int v1,int v2)
{
//降序让第一个数大于第二个数
return v1>v2;
}
l1.sort(mycompare);//sort是成员函数
所有元素都会在插入的时候自动被排序
本质:set/multiset属于关联式的容器,就是结构是用二叉树实现的。
set不允许数据中有重复存在,multiset允许数据的重复存在。
//构造:
sets;
set s1(const set& s2);
//插入
s1.insert(10);
//大小
size();
empty();
//交换
swap(st);
//插入
insert(elem);
//删除
clear();
//删除pop所指的元素,返回下一个元素的迭代器
erase(pop);
//删除begin,end的元素,返回下一个元素的迭代器
erase(begin,end);
//删除容器中为elem的元素
erase(elem);
//查找key是否存在,存在就返回该键元素的迭代器,不存在就返回set.end()
find(ket);
//统计key元素的个数
count(key);
队组:成对出现的数据,利用队组可以返回俩个数据
pairp(value1,value2);
pairp=make_pair(value1,value2);
//pair对组的创建
void test01()
{
//第一种方式
pairp("Tom",20);
cout<p2=make_pair("jerry",18);
}
map中的所有元素都是pair
pair中的第一个元素是key(键值),起到索引的作用。第二个元素是实值value
所有元素都会根据键值自动排序。
map本质上也是关联式的容器,底层结构是用二叉树实现的。
优点:可以根据key快速找到value
缺点:(1)map不允许有重复的key
(2)multimap允许出现重复的key
//构造
mapmp;
map(const map& mp);
//赋值
map& operator=(const map &mp);
//大小
size()
empty()
swap(st);
//插入
insert(item);
//清除
clear();
//删除pos迭代器所指的元素,返回下一个元素的迭代器
erase(pos);
//删除区间
erase(begin,end);
//删除容器中值为key的元素
erase(key);
//查找
find(key);
//统计
count(key);
map容器中的排序
#include
using namespace std;
#include
概念:
重载函数调用操作符的类,其对象称为函数对象
函数对象使用重载的()时候,行为类似函数调用,也叫做仿函数。
函数对象在使用的时候,可以像普通函数那样调用,可以有参数和返回值
int operator()(int v1,int v2)
{
return v1+v2;
}
myprintf()
{
this->count=0;
this->count++;
}
int operator()(int v1,int v2)
{
return v1+v2;
}
int count;//查看调用次数
函数对象可以作为参数传递
void doprint(myprint &mp,string test)
{
mp(test);
}
void test03()
{
myprint my;
doprint(my,"hello");
}
如果operator()接受一个参数,那么叫做一元谓词
如果operator()接受俩个参数,那么叫做二元谓词
class over
{
public:
bool operator()(int val)
{
return val>5;
}
};
void test01()
{
vectorv1;
int i=0;
for(;i<10;i++)
{
v.push_back(i);
}
//查找容器中是否有大于5的数字 over()匿名函数对象
vector::iterator it=find_if(v1.begin,vi.end,over());
if(it==v.end())
{
cout<<"未找到”<
二元谓词
bool operator()(int val1,int val2)
{
return val1>val2;
}
功能:实现四则运算
plus minus multiplies divides modulus negate//取反
#include
void test()
{
negate(n);
n(50);
}
void test()
{
plusp
p(10,20);
}
功能:实现关系对比
equal not_equal greater greater_equal less less_equal//
sort(v.begin,v.end,greater());//greater<>内建函数对象
功能:实现逻辑运算
logical_and与 logical_or或 logic_not非
//把v1容器中的数据搬到v2,并且执行取反操作
vectorv1
vectorv2;
v2.resize(v.size());
transform(v.begin(),v.end(),v2.begin(),logical_not())
//for_each遍历
#include
#include
class print{
public:
void operator()(int val)
{
cout<v;
for(int i=0;i<10;i++)
{
v.push_back(i);
}
for_each(v.begin(),v.end(),print());
}
//transform的遍历
class transform()
{
public:
int operator()(int val)
{
return val+10000;
}
};
void test01()
{
vectorv;
for(int i=0;i<10;i++)
{
v.push_back(i);
}
vectorv1;
v1.resize(v.size());
transform(v.begin(),v.end(),v1.begin(),transform());
}
find:查找指定元素,查到的话返回指定元素的迭代器,找不到的话返回最后元素的迭代器end()
vector::iterator it=find(v.begin(),v.end(),5);
if(it==v.end)
cout<<"没有找到"<m_name=name;
this->m_age=age;
}
string m_name;
int m_age;
//重载==
bool operator==(const person& p)
{
if(this->m_name==p.m_name&&this->m_name==p.m_name)
return true;
else return false;
}
}
find_if(起始,最后,仿函数):按条件查找元素
按照值去查找元素,找到的话就返回指定位置迭代器,找不到的话返回结束迭代器。
vector::iterator it=find_if(v.begin(),v.end(),overfive());
adjacent_find(起始,最后):查找相邻元素
如果查到相邻元素了,就返回相邻元素的第一个的迭代器
vector::iterator pos=adjacent_find(v.begin(),v.end());
binary_search:查找指定的元素是否存在(必须是一个有序序列)
bool ret=binary_search(v.begin(),v.end(),9);
count:统计元素的个数
int num=count(v.begin(),v.end(),40);
count_if:按照条件来统计元素个数
class over5()
{
public:
bool operator()(int val)
{
return val>20;
}
};
int num=count_if(v.begin(),v.end(),over5());
sort 对容器元素进行排序
sort(v.begin(),v.end(),greater());
random_shuffle 洗牌 在指定范围内的元素随机调整次序
void test()
{
srand((unsigned int)time(NULL));
}
random_shuffle(v.begin()
merge:容器元素合并 并且存储到另一个容器中 俩个容器必须是有序的
//提前给目标容器分配空间
vtarget.resize(v1.size()+v2.size());
merge(v1.begin(),v1.end(),v2.begin(),v2.end(),vtarget.begin());
reverse:反转指定范围内的元素
reverse(v.begin(),v.end());
.copy:容器中指定范围的元素拷贝到另一个容器中
v2.resize(v1.size());
copy(v1.begin(),v1.end,v2.begin());
replace:将容器中指定范围内的旧元素修改为新元素
replace(v1.begin(),v1.end(),20,2000);//把20替换成2000
.replace_if:容器内指定范围满足条件的元素替换为新元素
class greater30
{
public:
bool operator()(int val)
{
return val>=30;
}
};
replace_if(v.begin(),v.end(),greater30,3000);//把大于等于30的替换成3000
swap:互换俩个容器中的元素
swap(v1,v2);
accumulate:计算容器元素累计总和
int total=accumulate(v1.begin(),v1.end(),val);//val是初始值
fill:向容器中添加元素
fill(v.begin(),v.end(),100);
常用集合算法
求俩个容器的交集
//目标容器需要提前开辟空间,俩个容器取小的
vtarget.resize(min(v1.size(),v2.size());
vector::iterator it
=set_intersection(v1.begin(),v1.end(),v2.begin(),v2.end(),vtarget.begin());
求俩个容器的并集:俩个集合必须是有序的序列
vtarget.resize(v1.size()+v2.size());
vector::iterator it
=set_union(v1.begin(),v1.end(),v2.begin(),v2.end(),vtarget.begin());
求俩个容器的差集:从某个容器中看和另一个容器中不同的部分
vtarget.resize(max(v1.size(),v2.size());
vector::iterator it=set_difference(v1.begin(),v1.end(),v2.begin(),v2.end(),vtarget());