目录
1.5 increment(前进)/decrement(后退)/dereference(取值)
2.2.1SGI标准的空间配置器, std::allocator
2.2.3构造和析构基本工具:construct()和destroy()
2.2.5第一级配置器_malloc_alloc_template剖析
2.2.6第二级配置器 _default_alloc_template剖析
3.4.2迭代器相应型别之二:difference type
3.4.5迭代器相应型别之五:iterator_category
5.3.2hash table的桶子(buckets)与节点(nodes)
编辑 6.1.2质变算法mutating algorithms——会改变操作对象的的值
6.1.3非质变算法nonmutating algorithms——不改变操作对象的值
6.4.1equal、fill、fill_n、iter_swap、lexicographical_compare、max、min、mismatch、swap
1.STL概论与版本简介
1.1STL六大组件 功能与应用
一、容器:是各种数据结构,如vector、list、deque、set、map。从现实角度看STL容器是一种class template(类模板)
二、算法:常用算法如sort、search、copy、erase。从现实角度看STL算法算是一种function template(函数模版)
三、迭代器:扮演容器和算法之间的胶合剂,是“范型指针”。从现实角度看,迭代器是一种将指针相关操作予以重载的class template。
四、仿函数:行为类似函数。从现实角度看,仿函数是一种重载了operator()的class或class template。
五、配接器:一种用来修饰容器或仿函数或迭代器接口的东西,如STL提供的queue和stack
六、配置器:负责空间配置与管理。从现实角度看,配置器是一个实现了动态空间配置、管理和释放的class template
1.2六大组件的交互关系
遵循规范使用无拓展名头文件
1.3临时对象的产生于应用
临时对象是一种无名对象,临时对象的产生方法:在型别名称之后直接加一对小括号,并可指定初值,如shape(8),其意义相当于调用相应的constructor(构造函数)且不指定对象名称。运用于:仿函数与算法的搭配上面,例如
#include<vector>
#include<algorithm>
#include<iostream>
using namespace std;
template<typename T>
class print
{
public:
void operator()(const T& elem)//重载()
{cout<<elem<<' ';}
};
int main()
{
int ia[6]={0,1,2,3,4,5};
vector<int>iv{ia,ia+6};
//print<int>()是一个临时对象,不是一个函数调用操作
for_each(iv.begin(),iv.end(),print<int>());
}
1.4静态常量整数成员在class内部直接初始化
如果class内含有 const static integral(整数类别) 数据成员,那么可以在class内部直接初始化,例如:
#include<iostream>
using namespace std;
template<typename T>
class testClass
{
public:
static const int datai=5;
static const long datal=3L;
static const char datac='c';
};
int main()
{
cout<<testClass<int>::datai<<endl;
cout<<testClass<int>::datal<<endl;
cout<<testClass<int>::datac<<endl;
}
1.5 increment(前进)/decrement(后退)/dereference(取值)
increment/decrement操作符在迭代器的实现上占非常重要地位,因为每一个迭代器都要实现前进和取值功能,有些迭代器有双向移动功能还需要提供后退操作,例如
#include<iostream>
using namespace std;
class INT
{
friend ostream& operator<<(ostream& os,const INT& i);//重载<<
public:
INT(int i):m_i(i){};//构造函数
//prefix:increment and then increment (前进前置式:先前进后提取)
INT& operator++()//重载++
{
++(this->m_i);//前进 随着class的不同,该行应该有不同的操作
return *this;//提取
}
//postfix:fetch and then increment(前进后置式:先提取后前进)
const INT operator++(int)//重载++
{
INT temp=*this;//将内容保存在中间对象上
++(*this);//前进
return temp;//提取保存在中间对象里的内容
}
//prefix:decrement and then fetch(后退前置式,先后退再提取)
INT& operator--()//重载--
{
--(this->m_i);//后退 随着class的不同,该行应该有不同的操作
return *this;//提取
}
//prefix:decrement and then fetch(后退后置式,先提取再后退)
const INT operator--(int)//重载--
{
INT temp=*this;//将内容保存在中间对象上
--(*this);//后退
return temp;//提取保存在中间对象里的内容
}
//dereference(取值)
int& operator*()const
{
return (int&)m_i;
}
private:
int m_i;
};
ostream& operator<<(ostream& os,const INT& i)
{
os<<'['<<i.m_i<<']';
return os;
}
int main()
{
INT I(5);
cout<<I++;
cout<<++I;
cout<<I--;
cout<<--I;
cout<<*I;
}
1.6前闭后开区间表示法[ )
任何一个STL算法都需要由一对迭代器标识区间,用来表示操作范围,迭代器表示的区间,就是前闭后开区间
template<class InputIterator, class T>
InputIterator find(InputIterator first,InputIterator last,const T& value)
{
while(first != last && *first !=value)
{
++first;
}
return first;
}
template<class InputIterator, class Function>
InputIterator for_each(InputIterator first,InputIterator last,Function f)
{
for(;first != last;++first)
{
f(*first);
}
return f;
}
1.7function call 操作符
函数调用操作符也可以被重载,C语言通过函数指正达成目标,但是缺点是无法保持自己的状态,也没有可适配性。STL采用仿函数实现,例如
#include<iostream>
using namespace std;
//由于将operator()重载了,因此plus成了一个仿函数
template<class T>
struct plus1{
T operator()(const T& x,const T& y)const{return x + y;}
};
//由于将operator()重载了,因此minus成了一个仿函数
template<class T>
struct minus1{
T operator()(const T& x,const T& y)const{return x-y;}
};
int main()
{
//以下参数仿函数对象
plus1<int>plusobj;
minus1<int>minusobj;
//以下使用仿函数,就像使用一般函数一样
cout<<plusobj(3,5)<<endl;
cout<<minusobj(3,5)<<endl;
//以下直接产生仿函数的临时对象(第一对小括号),并调用(第二对小括号)
cout<<plus1<int>()(43,50)<<endl;
cout<<minus1<int>()(43,50)<<endl;
}
2.空间配置器
2.1设计一个简单的空间配置器部分详解
#include<new>
#include <cstddef>
#include<cstdlib>
#include<climits>
#include<iostream>
using namespace std;
namespace JJ
{
//分配空间
template<class T>
inline T* _allocate(ptrdiff_t size,T*){//inline内联函数、ptrdiff_t通常用来保存两个指针减法操作的结果,被定义为signed long int类型
set_new_handler(0);//作用是设置new_p指向的函数为new操作或new[]操作失败时调用的处理函数。默认情况下,分配失败时抛出bad_alloc异常
T* tmp=(T*)(::operator new((size_t)(size* sizeof(T))));//size_t用于指明数组长度,被定义为unsigned long int类型,operator new分配内存
if(tmp==0){ //分配空间失败时,输出一个标准错误,退出程序
cerr<<"out of memory"<<endl;
exit(1);
}
return tmp; //返回指向分配空间的指针
}
//释放空间
template<class T>
inline void _deallocate(T* buffer){
::operator delete(buffer);//operator delete释放内存
}
//构造
template<class T1,class T2>
inline void _construct(T1* p,const T2& value){
new(p)T1(value);
}
//析构
template<class T>
inline void _destroy(T* ptr){
ptr->~T();
}
}
2.2具备次配置能力的SGI空间配置器
SGI STL的配置器名称为alloc,而且不接受任何参数,但是使用时不用担心,因为每一个容器都已经指定了缺省的空间配置器为alloc。
2.2.1SGI标准的空间配置器, std::allocator
allocator只是基层内存配置/释放行为的一层薄薄的包装。不建议使用allocator,主要原因是效率不佳。
2.2.2SGI特殊的空间配置器,std::alloc
c++内存配置操作和释放操作new/delete
class Foo{...};
Foo* pf=new Foo; //配置内存,然后构造对象
delete pf; //将对象析构,然后释放内存
new算式内包含两阶段操作:
(1)调用::operator new 配置内存
(2)调用Foo::Foo()构造对象内容。
delete算式也包含两阶段操作:
(1)调用Foo::~Foo()将对象析构
(2)调用::operator delete释放内存
为了精密分工,STL allocator决定将这两阶段操作分开来。内存配置alloc::allocate(),内存释放alloc::deallocate(),对象构造::construct(),对象析构::destroy().
配置器定义与<memory>中,SGI<memory>内含<stl_alloc.h>、<stl_construct.h>
2.2.3构造和析构基本工具:construct()和destroy()
construct()接受一个指针p和一个初值value,该函数用途就是将初值设定到指针所指的空间上。
destroy()有两个版本,第一版本接受一个指针,然后将该指针指向的部分析构掉。第二版本接受first和last两个迭代器,将[first,last)范围内的所有对象都析构掉
2.24空间的配置与释放,std::alloc
SGI是以malloc()和free()完成内存的配置与释放的。考虑到小型区块可能造成的内存破碎问题,SGI设置了双层级配置器:(一)第一级直接使用malloc()和free()。(二)第二级视情况采用不同策略:1、配置区块超过128bytes时,调用第一级配置器。2、配置区块小于128bytes时,采用复杂的memory pool整理方式,不求助第一级配置器。整个设计究竟是只开放第一级配置器还是同时开放第二级配置器,取决于_USE_MAKKOC是否被定义。
2.2.5第一级配置器_malloc_alloc_template剖析
第一级配置器以malloc()、free()、realloc()等c函数执行实际的内存配置、释放、重配置操作,并实现类似的c++ new-handler机制。
c++ new-handler机制是:要求系统在内存配置需求无法满足时,调用一个指定的函数(set_new_handler())。
2.2.6第二级配置器 _default_alloc_template剖析
第二级配置器多了一些机制,避免太多小额区块造成内存碎片,配置时带来额外的负担。
做法是:区块超过128bytes,就移交第一级配置器处理。区块小于128bytes,则以内存池管理,此法又叫次层配置:每次配置一大块内存,并维护对应自由链表(free-list)。下次还有相同大小的内存需求直接从free-list拨出。如果客端释还小额区块,就由配置器回收到free-list中。第二级配置器会主动将小额区块的内存需求上调至8的倍数,并维护16个free-list。
2.2.7空间配置函数allocate()
标准接口函数allocate()。此函数首先判断区块大小,大于128就调用一级配置器,小于128就检查对应free list ,如果free list之内有可用的区块,就直接用,没有的话,就将区块大小上调至8的倍数,然后调用refill(),准备为free list 重新填充空间。
2.2.8空间释放函数deallocate()
标准接口函数deallocate():该函数首先判断区块大小,大于128调用一级配置器,小于128找出对应的free list,将区块回收。
2.2.9重新填充free lists
当free list中没有可用的区块时,就调用refill(),为free list 重新填充空间,新的空间将取自内存池,缺省取得20个新节点,但是内存池空间不够时,获得的节点数可能小于20.
2.2.10内存池(memory pool)
从内存池取空间给free list 使用,是chunk_alloc()的工作。chunk_alloc()函数以end_free和start_free来判断内存池的可用容量,如果容量充足,直接调用20个区块返回给free list,如果容量不足以提供20个区块,但还足够供应一个以上的区块,就返回这不足20个区块的空间出去。如果内存池连一个区块空间都无法供应,则利用malloc()从heap中配置内存,新配置内存的大小为需求大小的两倍加上一个随配置次数而越来越大的附加量。
如果连system heap空间都不够了,chunk_alloc()就到处寻找未用且足够大的区块提供给free list,找不到就调用第一级配置器,也找不到的话,就发出bad_alloc异常。
2.3内存基本处理工具
STL定义五个全局函数,作用于未初始化空间上。
(1)构造construct();(2)析构destroy();(3)(copy())uninitialized_copy();(4)(fill())uninitialized_fill();(4)(fill_n())uninitialized_fill_n();
2.3.1uninitialized_copy()
该函数能够将内存的配置与构造行为分离开。它会将指定范围元素拷贝到指定区域。如果输出目的地范围内的每一个迭代器都指向未初始化区域,则uninitialized_copy()会使用copy constructor。给输入来源范围内的每一个对象产生一份复制品,放进输入范围。
容器全区间构造函数步骤:
(1)配置内存区块,足以包含范围内的元素。
(2)使用uninitialized_copy(),在该内存区块上构造函数。
2.3.2uninitialized_fill()
该函数也可以将内存配置与对象构造分离开来,它会为指定范围填充元素,步骤与2.3.1一样。
2.3.3uninitialized_fill_n
该函数也可以将内存配置与对象构造分离开来,它会为指定范围内的所有元素设定相同的值,步骤与2.3.1一样。
上述三个函数的具体实现还是看c++参考文档吧。
3迭代器概念与traits编程技法
3.1迭代器设计思维-STL关键所在
STL中心思想的中心思想在于:将数据容器和算法分开,彼此独立设计,最后再用迭代器将他们撮合在一起。以find()函数为例,以下例子看不看无所谓。
template<class InputIterator,class T>
InputIterator find(InputIterator first,InputIterator last,const T& value){
while(first != last && *first != value){
++first;
}
return first;
}
#include <vector>
#include <list>
#include <deque>
#include <algorithm>
#include <iostream>
using namespace std;
int main(){
const int arraySize=7;
int ia[arraySize]={0,1,2,3,4,5,6};
vector<int>ivect(ia,ia+arraySize);
list<int>ilist(ia,ia+arraySize);
deque<int>ideque(ia,ia+arraySize);
vector<int>::iterator it1=find(ivect.begin(),ivect.end(),4);
if(it1==ivect.end()){
cout<<"没有找到4"<<endl;
}
else{
cout<<"找到了4="<<*it1<<endl;
}
list<int>::iterator it2=find(ilist.begin(),ilist.end(),6);
if(it2==ilist.end()){
cout<<"没有找到6"<<endl;
}
else{
cout<<"找到了6="<<*it2<<endl;
}
deque<int>::iterator it3=find(ideque.begin(),ideque.end(),8);
if(it3==ideque.end()){
cout<<"没有找到8"<<endl;
}
else{
cout<<"找到了8="<<*it3<<endl;
}
}
3.2迭代器是一种smart pointer(智能指针)
迭代器是一种行为类似指针的对象,指针最常见也最重要的内容是内容提取和成员访问,所以迭代器最重要的工作就是对operator*和operator->进行重载工作。
为list设计一个迭代器:以下不是完整的list迭代器代码,无需纠结。
#include<iostream>
template<typename T>
class ListItem //链表项
{
public:
T value() const {return _value;}
ListItem* next() const{return _next;}
//...
private:
T _value;
ListItem* _next; //单链表
};
template<typename T>
class List //双链表
{
void insert_front(T value); //头部插入
void insert_end(T value); //尾部插入
void display(std::ostream &os=std::cout)const; //成员访问
//...
private:
ListItem<T>* _end;
ListItem<T>* _front;
long _size;
};
将这个List套用到之前的find(),需要设计一个迭代器,当我们 提领迭代器时,传回的应该是LIstItem对象;递增该迭代器,应该指向下一个LIstItem对象;
template<class Item>//Item可以是单向链表节点或双向链表节点。
struct ListIter //此处这个迭代器特定只为链表服务
{
Item* ptr; //保持与容器之间的一个联系
ListIter(Item* p=0):ptr(p){}
Item& operator*() const{return *ptr;}//内容提取
Item* operator->() const{return ptr;}//成员访问
ListIter& operator++() //前置
{ptr = ptr->next();return *this;}
ListIter& operator++(int) //后置
{ListIter tmp=*this;++*this;return tmp;}
bool operator==(const ListIter& i)const
{return ptr==i.ptr;}
bool operator!=(const ListIter& i)const
{return ptr !=i.ptr;}
将list和find()由ListIter粘和起来:
mylist.front()这个函数没有在书上代码中出现,List里面的函数书上也没有具体实现,所以以下代码是有问题的。有余力的人可以自行尝试补完代码。
template<typename T>
bool operator!=(const ListItem<T>& item,T n)
{return item.value()!=n;}
int main()
{
List<int>mylist;
for(int i=0;i<5;++i)
{
mylist.insert_front(i);
mylist.insert_end(i+2);
}
mylist.display(); //输出链表里面的内容
ListIter<ListItem<int>>begin(mylist.front());//我没有找到front函数。front()函数的作用为返
//回指向链表头部元素
ListIter<ListItem<int>>end;
ListIter<ListItem<int>>iter;
iter=find(begin,end,3);
if(iter==end){cout<<"找不到"<<endl;}
else{cout<<"找到"<<iter->value()<<endl;}
iter=find(begin,end,7);
if(iter==end){cout<<"找不到"<<endl;}
else{cout<<"找到"<<iter->value()<<endl;}
}
3.3迭代器相应类别
假设算法中要以所指对象的型别为型别:解决办法是利用function template的参数推导,不过该方法只适用一部分情况,如无法推导函数的传回值。
template<class I,class T>
void func_impl(I iter,T t)
{
T tmp; //这里解决了问题。T就是迭代器所指之物的类别,本例为int
//...这里做原本func()应该做的全部工作
}
template<class I>
inline
void func(I iter)
{
func_impl(iter,*iter); //func的工作全部移往func_impl
}
int main()
{
int i;
func(&i);
}
3.4Traits编程技法——STL源代码门钥
我理解特化编程写法,就是在泛型模板基础上对各种特殊情况设计出不同的特定化模板。
//泛型模板
template< typename T>
class C{...};
//特化版本
template< typename T>//这个特化版本仅适用于"T为原生指针"的情况
class C<T*>{...};
//用来萃取迭代器的特性
template<class I>
struct iterator_traits{
typedef typename I::value_type value_type;
};
template<class I>
typename iterator_traits<I>::value_type //这一行是函数返回类别
func(I ite)
{return *ite;}
template<class T>
struct iterator_traits<T*>{ //偏特化版本——迭代器是个原生指针
typedef T value_type;
};
template<class T>
struct iterator_traits<const T*>{ //偏特化版本——当迭代器是个pointer-to-const,萃取出来的应该是个T而非const T
typedef T value_type;
};
最常用的迭代器相应类型分别有五种:value type、difference type、pointer、reference、iterator category。特性萃取机(原味榨汁机)traits。
template<class I>
struct iterator_traits{
typedef typename I::iterator_category iterator_category;
typedef typename I::value_type value_type;
typedef typename I::difference_type difference_type;
typedef typename I::pointer pointer;
typedef typename I::reference reference;
};
3.4.1迭代器相应型别之一:value type
value type指的是迭代器所指对象的型别。定义自己的value type内嵌型别。内嵌型别代码该在3.4,但是我没写,诶嘿。所以现在写吧:
template<class T>
struct MyIter{
typedef T value_type;
T* ptr;
MyIter(T* p=0):ptr(p);//内嵌型别声明
T& operator*() const{return *ptr;}
//...
};
template<class I>
typename I::value_type //这一整行是func的返回值型别
func(I ite)
{return *ite;}
//...
MyIter<int>ite(new int(8));
std::cout<<func(iter);
3.4.2迭代器相应型别之二:difference type
difference type用来表示两个迭代器之间的距离,所以他也可以用来表示一个容器的最大容量。如果一个泛型算法提供技术功能,那么他的传回值就必须使用迭代器的difference type
template<class I,class T>
typename iterator_traits<I>::difference_type
count(I firsr, I last,const T& value)
{
typename iterator_traits<I>::difference_type n=0;
for(;first!=last;++first){
if(first==value){
++n;
}
}
return n;
}
还有两种特化版本一种是针对原生指针,还有一种是针对pointer-to-const,代码和3.4的特化代码及其类似,不再重复写了。
3.4.3迭代器相应型别之三:reference type
在c++中,函数如果要传回左值,都以by reference的方式进行,所以当p是一个mutable iterators(允许改变所指对象之内容)时 ,如果其value type是T,那么*p的型别不应该是T,应该是T&。具体实现在下一小节。
3.3.4迭代器相应型别之四:pointer type
pointer是传回一个令他代表p所指之物,reference是传回一个令他代表p所指之物的地址
Item& operator*() const{return *ptr};
Item& operator->() const{return ptr};
template<class I>
struct iterator_traits{
// ...
typedef typename I::pointer pointer;
typedef typename I::reference reference;
}
//针对原生指针偏特化版
template<class T>
struct iterator_traits<T*>{
...
typedef T* pointer;
typedef T& reference;
};
//针对原生的pointer-to-const偏特化版
template<class T>
struct iterator_traits<const T*>{
...
typedef const T* pointer;
typedef const T& reference;
};
3.4.5迭代器相应型别之五:iterator_category
迭代器根据移动特性与施行操作,迭代器被分为五类。
(1)Input Iterator:只读
(2)output Iterator:只写
(3)Forward iterator:允许“写入型”算法(例如 replace())在此种迭代器所形成的区间上进行读写操作
(4)Bidirectional Iterator:可双向移动。某些算法需要逆向走访某个迭代器区间
(5)Random Access Iterator:前四种只有一部分算数能力,第五种包含所有指针算数能力。
3.5std::iterator的保证
就是设计迭代器时需要提供五个内嵌相应型别,简单做法是,直接继承STL提供的iterators class。
__type_traits部分看着实在头疼暂时先放弃(虽然可能是永久放弃)。
4序列式容器
4.1容器的概观与分类
容器,置物之所也。(这句我很喜欢,我理解的意思为,容器是用来放置物品的地方)
任何特定的数据结构都是为了实现某种特定的算法,STL容器就是将运用最为广泛的一些数据结构实现出来。
根据数据在容器中的排列特性,将这些数据结构分为序列式和关联式两种。
衍生是内含关系的意思,例如heap内含一个vector。
4.1.1序列式容器
所谓序列式容器,其中的元素都可序,但未必有序,c++本身提供了array,STL另外提供vector、list、deque、stack、queue、priority-queue等序列容器,其中stack和queue由于只是将deque改头换面而成,技术上被归类为一种配接器。
4.2vector
4.2.1vector概述
vector的数据安排和操作方式,与array非常类似,两者唯一差别在于对空间的运用的灵活性。array是静态空间,一旦配置就无法改变。vector是动态空间,随着元素加入,他的内部机制会自动扩充空间容纳新元素。
vector实现技术,关键在于其对大小的控制以及重新配置时的数据移动效率。不可能客户端新增一个元素,vector内部只是拓展一个元素的空间。具体空间配置策略在下文。
4.2.2vector定义摘要
vector 是一个十分有用的容器。它能够像容器一样存放各种类型的对象。也就是说,vector是一个能够存放任意类型的动态数组。
vector 是同一种类型的对象的集合,每个对象都有一个对应的整数 索引值。和 string 对象一样,标准库将负责管理与存储元素相关的内存。 把 vector 称为容器,是因为它可以包含其他对象。一个容器中的所有对象都必须是同一种类型的。
书上内容部分全是源代码摘录,我本来想去复制粘贴vector的源代码的,但是找到的都和书上有出入,于是我也去复制粘贴书上源代码了,后面类似部分也是如此。(主要是实在太多了,要死要死)。
4.2.3vector的迭代器
vector维护的是一个连续线性空间,并且vector迭代器所需要的操作,普通指针都可以满足,所以vector提供的是Random Access Iterator。
template<class T,class Alloc::alloc>
class vector{
public:
typedef T value_type;
typedef value_type* iterator; //vector的迭代器是普通指针
};
4.2.4vector的数据结构
vector采用的数据结构为:连续线性空间。它以两个迭代器start和finish分别指向配置得来的连续空间中已经被使用的范围,并以迭代器end_of_storage指向整个连续空间(含备用空间)的尾端:
template<class T,class Alloc::alloc>
class vector{
...
protected:
iterator start; //表示目前使用的空间头
iterator finish; //表示目前使用的空间尾
iterator end_of_storage; //表示目前可用空间的尾
...
};
为了降低空间配置时的速度成本,vector实际配置的大小可能比客户端需求量更大一些,以备将来可能的扩充。这便是容量的概念。
4.2.5vector的构造与内存管理
以下的测试程序,观察重点在构造的方式,元素的添加,以及大小容量的变化。书上代码从倒数第11行开始有问题,本文章代码做了修改。
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
int i;
vector<int>iv(2,9);
cout<<"size="<<iv.size()<<endl; //size=2 返回容纳的元素数
cout<<"capacity="<<iv.capacity()<<endl; //capacity=2 返回当前空间能够容纳的元素
iv.push_back(1);
cout<<"size="<<iv.size()<<endl; //size=3
cout<<"capacity="<<iv.capacity()<<endl; //capacity=4
iv.push_back(2);
cout<<"size="<<iv.size()<<endl; //size=4
cout<<"capacity="<<iv.capacity()<<endl; //capacity=4
iv.push_back(3);
cout<<"size="<<iv.size()<<endl; //size=5
cout<<"capacity="<<iv.capacity()<<endl; //capacity=8
iv.push_back(4);
cout<<"size="<<iv.size()<<endl; //size=6
cout<<"capacity="<<iv.capacity()<<endl; //capacity=8
for (i=0;i<iv.size();++i){
cout<<iv[i]<<' '; //9 9 1 2 3 4
}
cout<<endl;
iv.push_back(5);
cout<<"size="<<iv.size()<<endl; //size=7
cout<<"capacity="<<iv.capacity()<<endl; //capacity=8
for (i=0;i<iv.size();++i){
cout<<iv[i]<<' '; //9 9 1 2 3 4 5
}
cout<<endl;
iv.pop_back();
iv.pop_back();
cout<<"size="<<iv.size()<<endl; //size=5
cout<<"capacity="<<iv.capacity()<<endl; //capacity=8
iv.pop_back();
cout<<"size="<<iv.size()<<endl; //size=4
cout<<"capacity="<<iv.capacity()<<endl; //capacity=8
vector<int>::iterator ivite=find(iv.begin(),iv.end(),1);
if(*ivite==1){
iv.erase(ivite);
}
cout<<"size="<<iv.size()<<endl; //size=3
cout<<"capacity="<<iv.capacity()<<endl; //capacity=8
for (i=0;i<iv.size();++i){
cout<<iv[i]<<' '; //9 9 2
}
cout<<endl;
auto ite=find(iv.begin(),iv.end(),2);
if(*ite==2){
iv.insert(ite,3,7);
}
cout<<"size="<<iv.size()<<endl; //size=6
cout<<"capacity="<<iv.capacity()<<endl; //capacity=8
for (i=0;i<iv.size();++i){
cout<<iv[i]<<' '; //9 9 7 7 7 2
}
cout<<endl;
iv.clear();
cout<<"size="<<iv.size()<<endl; //size=0
cout<<"capacity="<<iv.capacity()<<endl; //capacity=8
}
vector缺省使用了alloc作为空间配置器,并据此另外定义了一个data_allocator,为的是方便以元素大小为配置单位。data_allocator::allocate(n)表示配置了n个空间。(simple_alloc()见书上2.2.4节)。
template<class T,Alloc::alloc>
class vector{
protected:
typedef simple_alloc<value_type,Alloc>data_allocator:
...
};
书上构造函数部分详细讲解了构造的实现原理,但是我感觉用处不大,所以替换成其他笔记。后面部分都会如此,不会按照书上的目录来写了。
利用构造函数初始化
vector():创建一个空vector vector(int nSize):创建一个vector,元素个数为nSize
vector(int nSize,const t& t):创建一个vector,元素个数为nSize,且值均为t
vector(const vector&):复制构造函数
vector(begin,end):复制[begin,end)区间内另一个数组的元素到vector中
vector vec(10);//定义一个含10个变量的整型数组vec,默认值都为0
vector vec(10,2);//定义一个含10个变量的整型数组vec,默认值都为2
vector vec(a);//其中a也是vector,把a整体赋值给vec
vector vec(a.begin(),a.begin+1);//把a的从开始到第二个赋值给vec
vector容器之遍历函数
reference at(int pos):返回pos位置元素的引用
reference front():返回首元素的引用
reference back():返回尾元素的引用
iterator begin():返回向量头指针,指向第一个元素
iterator end():返回向量尾指针,指向向量最后一个元素的下一个位置
reverse_iterator rbegin():反向迭代器,指向最后一个元素
reverse_iterator rend():反向迭代器,指向第一个元素之前的位置
vector容器之增加函数
void push_back(const T& x)
//向量尾部增加一个元素X
iterator insert(iteratorit,const T& x)vector容器之删除函数
//向量中迭代器指向元素前增加一个元素x
iterator insert(iterator it,int n,const T& x)
//向量中迭代器指向元素前增加n个相同的元素x
iterator insert(iterator it,const_iterator first,const_iterator last)
//向量中迭代器指向元素前插入另一个相同类型向量的[first,last)间的数据
vector容器之删除函数
iterator erase(iterator it):删除向量中迭代器指向元素
iterator erase(iterator first,iterator last):删除向量中[first,last)中元素
void pop_back():删除向量中最后一个元素 void clear():清空向量中所有元素
vector容器之判断函数
bool empty() const:判断向量是否为空,若为空,则向量中无元素
vector容器之大小函数
int size() const:返回向量中元素的个数
int capacity() const:返回当前向量所能容纳的最大元素值
int max_size() const:返回最大可允许的 vector 元素数量值
vector容器之其他函数
void swap(vector&) 交换两个同类型向量的数据
void assign(int n,const T& x) 设置向量中前n个元素的值为x
void assign(const_iterator first,const_iterator last) 向量中[first,last)中元素设置成当前向量元素
4.3list
4.3.1list概述
list是一种序列式容器。list容器完成的功能实际上和数据结构中的 双向链表是极其相似的,list中的数据元素是通过链表指针串连成逻辑意义上的线性表,也就是list也具有链表的主要优点,即:在 链表的任一位置进行元素的插入、删除操作都是快速的。list容器每次插入或删除一个元素,就配置或者释放一个元素空间,对空间的运用绝对精准,一点也不浪费。
4.3.2list的节点
template<class T>
struct _list_node {
typedef void* void_pointer;
void_pointer prev;
void_pointer next;
T data;
};
显然这是一个双向链表
4.3.3list的迭代器
因为list的节点在存储空间不能保证连续性,所以不能用普通指针作为迭代器,由于list是一个双向链表,迭代器必须具备前移后移的能力,所以list提供的是Bidirectional Iterator。
list有一个重要性质,就是插入操作、接合和操作和元素删除操作都不会导致原有迭代器全部失效。
4.3.4list的数据结构
list不仅是一个双向链表,还是一个环状双向链表,所以只需要一个指针就可以完整表现整个链表。
4.3.5list容器构造函数
list l1; //创建一个空链表
list l2(10); //创建一个链表其有10个空元素
list l3(5,20); //创建一个链表其有5个元素内容为20
list l4(l3.begin(),l3.end()); //创建一个链表其内容为l3的内容
list l5(l4); //创建一个链表其内容为l4的内容
4.3.6list容器函数列表
Lst1.assign() 给list赋值
Lst1.back() 返回最后一个元素
Lst1.begin() 返回指向第一个元素的迭代器
Lst1.clear() 删除所有元素
Lst1.empty() 如果list是空的则返回true
Lst1.end() 返回末尾的迭代器
Lst1.erase() 删除一个元素
Lst1.front() 返回第一个元素
Lst1.get_allocator() 返回list的配置器
Lst1.insert() 插入一个元素到list中
Lst1.max_size() 返回list能容纳的最大元素数量
Lst1.merge() 合并两个list list容器函数列表
Lst1.pop_back() 删除最后一个元素
Lst1.pop_front() 删除第一个元素
Lst1.push_back() 在list的末尾添加一个元素
Lst1.push_front() 在list的头部添加一个元素
Lst1.rbegin() 返回指向第一个元素的逆向迭代器
Lst1.remove() 从list删除元素
Lst1.remove_if() 按指定条件删除元素
Lst1.rend() 指向list末尾的逆向迭代器
Lst1.resize() 改变list的大小
Lst1.reverse() 把list的元素倒转
Lst1.size() 返回list中的元素个数
Lst1.sort() 给list排序
Lst1.splice() 合并两个list
Lst1.swap() 交换两个list
Lst1.unique() 删除list中重复的元素
4.4deque
4.4.1deque概述
deque使一种双向开口的连续线性空间,双向开口意思就是可以在头尾两端都做元素的插入和删除操作。
deque双端队列,可以对头端和尾端进行插入删除操作。
deque队列为一个给定类型的元素进行线性处理,像向量一样,它能够快速地随机访问任一个元素,并且能够高效地插入和删除容器 的尾部元素。但它又与vector不同,deque支持高效插入和删除容器的头部元素。
deque允许常数时间内对头端进行元素的插入或移除操作,deque没有所谓的容量观念,因为它是动态的以分段连续空间组合而成随时可以增加一段新空间并链接起来。deque的迭代器十分复杂,所以能用vector就用vector吧。
4..4.2deque的中控器
deque为了维持整体连续空间的假象,需要一个中央控制。deque采用一小块所谓的map作为主控,这里的map是一小块连续空间,其中每一个元素都是指针,指向一处连续线性空间,称为缓冲区。缓冲区才是deque的存储空间主体。map其实是一个T**,也就是说它是一个指针,所指之物又是一个一个指针,指向型别为T的一块空间。
4.4.3deque的迭代器
deque迭代器应该具备什么结构?。第一:他必须能够指出分段连续空间在那边,第二:它必须能判断自己是否处于缓冲区的边缘,如果是的话,一旦前进或者后退就需要跳跃到上一个缓冲区或下一个缓冲区。
template<class T,class Ref,class Rtr,size_t BufSiz>
struct _deque_iterator {
//...一堆迭代器相应型别
//保持与容器的联结
T* cur; //指向缓冲区的现行元素
T* first; //指向缓冲区的头
T* last; //指向缓冲区的尾(含备用空间)
map_pointer node; //指向管控中心map(map_pointer是T**)
};
4.4.4deque的数据结构
deque需要维护指向map的指针、start和finish两个迭代器还需要记住map的大小。
4.4.5deque容器构造函数
deque deqT; //默认构造形式
deque(beg, end); //构造函数将[beg,end]区间中的元素拷贝给本身
deque(n, elem); //构造函数将n个elem拷贝给本身
deque(const deque &deq); //拷贝构造函数
deque a; // 定义一个int类型的双端队列a
deque a(10); // 定义一个int类型的双端队列a,并设置初始大小为10
deque a(10, 1); // 定义一个int类型的双端队列a,并设置初始大小为10且初始值都为1
deque b(a); // 定义并用双端队列a初始化双端队列b
deque b(a.begin(), a.begin()+3); // 将双端队列a中从第0个到第2个(共3个)作为双端队列b的初始值
4.4.6deque容器函数列表
d.push_front(const T& x); //头部添加元素
d.push_back(const T& x); //末尾添加元素
d.insert(iterator it, const T& x); //任意位置插入一个元素
d.insert(iterator it, int n, const T& x); //任意位置插入 n 个相同元素
d.insert(iterator it, iterator first, iterator last); //插入另一个向量的 [forst,last] 间的数据
d1.pop_front(); //头部删除元素
d1.pop_back(); //末尾删除元素
d1.erase(iterator it); //任意位置删除一个元素
d1.erase(iterator first, iterator last); //删除 [first,last] 之间的元素
d1.clear(); //清空所有元素:
d.size(); //容器大小
d.max_size(); //容器最大容量
d.resize(); //更改容器大小
d.empty(); //容器判空
d.shrink_to_fit(); //减少容器大小到满足元素所占存储空间的大小
deq.assign(int nSize, const T& x); //多个元素赋值 ,类似于初始化时用数组进行赋值
swap(deque&);//交换两个同类型容器的元素
下标访问:deq[1]; // 并不会检查是否越界
at 方法访问:deq.at(1); // 以上两者的区别就是 at 会检查是否越界,是则抛出 out of range异常
访问第一个元素:deq.front();
访问最后一个元素:deq.back();
4.5stack
4.5.1stack概述
stack(栈)容器是一种先进后出的容器,两端只有一个开口,只能从 这一个开口插入和删除数据。stack可以以deque为底部结构并封闭其头端开口,来实现stack的功能。
4.5.2stack构造函数
构造函数 stack stkT;//stack采用模板类实现, stack对象的默认构造形式:
stack(const stack &stk);//拷贝构造函数
赋值函数 stack&operator=(const stack &stk);//重载等号操作符
4.5.3stack函数列表
push(elem);//向栈顶添加元素
pop();//从栈顶移除第一个元素
top();//返回栈顶元素
empty();//判断堆栈是否为空
size();//返回堆栈的大小
4.6queue
4.6.1queue概述
Queue是一种先进先出(First In First Out,FIFO)的数据结构,它有两个出口。queue可以以queue为底部结构并封闭其底端出口和前端入口,来实现queue功能。
4.6.2queue构造函数
queue queT;//queue采用模板类实现,queue对象的默认构造形式:
queue(const queue &que);//拷贝构造函数
4.6.3queue函数列表
push(elem);//往队尾添加元素
pop();//从队头移除第一个元素
back();//返回最后一个元素
front();//返回第一个元素
empty();//判断队列是否为空
size();//返回队列的大小
4.7heap(隐式表达)
这段建议直接读书上内容,反而会更好理解。
heap是priority queue的助手。binary max heap适合作为priority底层机制。binary heap就是一种complete binary tree(完全二叉树),根据元素排列方式heap分为max_heap和min_heap,STL供应的是max_heap(最大值在根节点,并总是位于array或vector的起头处)。完成功能需要以vector作为底部容器和heap算法;
4.7.1heap算法
push_heap算法
就是完全二叉树添加元素的算法。
pop_heap算法
就是完全二叉树删除元素的算法。注意一点的是,在pop_heap后,最大元素只是被放置于底部容器的最尾端,尚未被取走。如果要取其值,可以使用底部容器vector提供的操作函数。
sort_heap算法
每次pop_heap都可以得到heap中键值最大的元素,一直用pop_heap操作就可以实现排序功能了。
make_heap算法
这个算法是用来将一段数据转化为一个heap,主要依据就是complete binary tree的隐式表达
4.7.2heap测试实例
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main()
{
int ia[9] = { 0,1,2,3,4,8,9,3,5 };
vector<int>ivec(ia, ia + 9);
make_heap(ivec.begin(), ivec.end());//将存在vector中的数据,转变成以heap方式存放。
for (int i = 0; i < ivec.size(); ++i)
{
cout << ivec[i] << ' '; //9 5 8 3 4 0 2 3 1
}
cout << endl;
ivec.push_back(7); //插入数字7
push_heap(ivec.begin(), ivec.end());
for (int i = 0; i < ivec.size(); ++i)
{
cout << ivec[i] << ' '; //9 7 8 3 5 0 2 3 1 4
}
cout << endl;
pop_heap(ivec.begin(), ivec.end());//将最大值放到vector容器底部
cout << ivec.back() << endl;
ivec.pop_back();
for (int i = 0; i < ivec.size(); ++i)
{
cout << ivec[i] << ' '; //8 7 4 3 5 0 2 3 1
}
cout << endl;
sort_heap(ivec.begin(), ivec.end());//对heap中的数据进行排序
push_heap(ivec.begin(), ivec.end());
for (int i = 0; i < ivec.size(); ++i)
{
cout << ivec[i] << ' '; //0 1 2 3 4 5 6 7 8
}
cout << endl;
}
4.8priority_queue
4.8.1priority_queue概述
priority_queue是一个拥有权值观念的queue,它允许加入新元素、移除旧元素、审视元素值等功能。只允许底端加入元素,顶端取出元素。
缺省情况下priority_queue系列利用一个max_heap完成工作,max_heap可以满足priority_queue需要的“依权值高低自动递增排序”的特性。并且是以vector为底部容器。
4.8.2priority_queue测试实例
#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;
int main()
{
int ia[9] = { 0,1,2,3,4,8,9,3,5 };
priority_queue<int>ipg(ia, ia + 9);
cout << "size=" << ipg.size() << endl; //size=9
for (int i = 0; i < ipg.size(); ++i) {
cout << ipg.top() << ' '; //9 9 9 9 9 9 9 9 9
}
cout << endl;
for (int i = 0; i < ipg.size(); ++i) {
cout << ipg.top() << ' '; //9 8 5 4 3 3 2 1 0
ipg.pop();
}
cout << endl;
}
4.9slist
4.9.1slist概述
slist是一个单向链表,slist和list的主要区别在于,前者迭代器属于单向的Forward iterator,后者的迭代器属于双向的Bidirectional Iterator。他们相同的特色是,他们的插入、移除、结合等操作不会导致原有迭代器失效。
4.9.2slist的节点
slist的节点要比list的复杂,运用了继承关系。
4.9.3slist的迭代器
slist迭代器如下图所示
他的数据结构和元素操作没什么好说的,就不在赘述了。
数据结构就是一个单向链表,操作也就push_front()头部插入元素、size()计算容器大小、begin()和end()开始和结束的迭代器、find()寻找指定元素、insert()插入元素(记得插入元素的位置只能在尾部)推荐使用insert_after()和erase_after()来完成插入和删除。
5关联式容器
标准STL关联式容器分为set(集合)和map(映射表)两大类,以及这两大类的衍生体multiset(多键集合)和multimap(多键映射表),这些容器底层均以红黑树完成。
此外STL还提供了一个不在标准规格之列的关联式容器:hash table(散列表),以及以此hash table为底层机制而完成的hash_set(散列集合)、hash_map(散列映射表)、hash_multiset(散列多键集合)、hash_multimap(散列多键映射表)。
关联式容器,观念上类似于关联式数据库:每个元素都有一个键值(key)和一个实值(value),当元素被插入到关联式容器中时,容器内部结构便按照便按照其键值大小,以某种特定规则将这个元素放置于适当位置。要想深入了解关联式容器,请先去把数据结构里面树的部分搞定吧。
5.1set/multiset
set的特性是,所有元素都会根据元素的键值自动排序。因为set元素的键值就是实值,实值就是键值,所以set容器中不允许有重复的元素。
不可以通过set的迭代器改变set的元素值,因为在set中的元素值就是键值,如果可以随意更改set的元素值,会破坏set的组织。set iterator是一种constant iterator。
set和list相同的性质是:当对容器中的元素做新增或者删除操作时,操作之前的所有迭代器,在操作完成后仍然有效。
标准的STL set是以RB_tree为底层机制,所以set的所有操作行为,都只是转调RB_tree的操作行为。
5.1.1set的构造和赋值
set<T> st 默认构造函数
set(const set &st) 拷贝构造函数
set& operator=(const set &st) 重载等号操作符
5.1.2set容器函数列表
insert(elem) 在容器中插入元素
clear() 清除所有元素
erase(pos) 删除pos迭代器所指的元素,返回下一个元素的迭代器
erase(beg,end) 删除区间[beg,end)的所有元素,返回下一个元素的迭代器
erase(elem) 删除容器中值为elem的元素
size() 返回容器中元素的数目
empty() 判断容器是否为空
swap(st) 交换两个集合容器
find(key) 查找key是否存在,若存在返回该键的元素的迭代器;若不存在,返回set.end()
count(key) 统计key的元素个数
5.1.3multiset
multiset的特性以及用法和set完全相同,唯一差别在于他允许键值重复。
Multiset是set集合容器的一种,其拥有set的全部内容,在此基础之上, multiset还具备了可以重复保存元素的功能,因此会有略微和set的差别。
Multise容器在执行insert()时,只要数据不是非法数据和空数据,insert就 总是能够执行,无论时一个数据还是一段数据。
Multiset容器中的find()函数回返回和参数匹配的第一个元素的迭代器,即 时存在多个元素也只是返回第一个,如{10,20,20,20}搜索20进行匹配将会 返回第二个参数,如果没有符合的参数则结束迭代器。
同理诸如lower_bound()等的需要进行一个位置的返回值,则统统返回第 一个发现的值。
5.2map/multiamp
map容器中所有元素都是pair,pair中第一个元素为key(键值),起到索引作用,第二个元素为value(实值)。
同时,所有元素都会根据元素的键值自动排序。
map/multimap属于关联式容器,底层数据结构是用二叉树实现的。 它的优点就是可以根据key值快速找到value值。
这里需要了解map与multimap的区别: 即map不予许容器中有重复的key值元素;而multimap允许容器中 有重复的key值元素,这里的区别与set与multiset十分类似。
5.2.1pair对组
标准头文件 #include<utility>
vs里面,某些编译器可以不声明这个头文件而直接使用,貌似在 C++中,pair被放入了std命名空间中了。
pair只含有两个元素,可以看作是只有两个元素的结构体。对于成对出现的数据,利用对组可以返回两个数据。
应用:代替二元结构体、作为键值对进行插入
在创建pair对象时,必须提供两个类型名,两个对应的类型名的类 型不必相同,可以在定义时进行成员初始化。
pair初始化:
格式为:template<class T1,class T2> struct pair;
在现实情况中我们可以像类似于STL创建新容器一样创建pair也可以直接使用,如下:
pair<int,int> p;
pair<int,int> p(10,20);
或者是
map<char,int> m;
m.insert(pair<char,int>('a',10));
pair数据访问:
//数据类型 数据
pair<string,int>p("地灵殿",18);
cout<<"姓名"<<p.first<<"年龄"<<p.second<<endl;
pair<string,int>p1=make_pir("地灵殿",18);
cout<<"姓名"<<p.first<<"年龄"<<p.second<<endl;
make_pir:
函数原型template pair make_pair(T1 a, T2 b) { return pair(a, b); }
可以通过make_pair生成我们的所需要的pair,对于一般的pair而言,如果需要对其进行赋值, 则需要
pair<int,int> p;
p.first=10,p.second=20;
但如果使用make_pair方法,则可以变成如下内容:
pair p; p=make_pair(10,20);
可以看见,使用make_pair不仅仅让我们免去了对两个变量进行分开来的访问赋值,同时 make_pair也智能的接受变量的类型,不需要再度指定,也就是说,make_pair本身是接受隐式 类型转换的,比如定义的是一个int类型,使用make_pair传入一个float类型的参数,make_pair 不会报错,而是回自动的进行一个类型转换,将float变为int,这样可以获得更高的灵活度, 同时也会有一些小问题
5.2.2map容器的构造与赋值
map<T,T> m map默认构造函数
map(const map &mp) 拷贝构造函数
map& operator=(const map &mp) 重载等号操作符
5.2.3map容器函数列表
size() 返回容器中元素的数目
empty() 判断容器中是否为空
swap(st) 交换两个集合容器
insert(elem) 在容器中插入元素
clear() 清除所有元素
erase(pos) 删除pos迭代器所指的元素,返回下一个元素的迭代器
erase(beg,end) 删除区间[beg,end)的所有元素,返回下一个元素的迭代器
erase(key) 删除容器中值为key的元素
find(key) 查找key是否存在,返回该键的元素的迭代器;若不存在返回map.end()
count(key) 统计key的元素的个数
5.3hashtable
这部分内容还是强烈推荐看书了,这部分我是直接看书直接理解的,很多都不太行。建议先学完数据结构上的哈希表再来看这个就很清楚了。
5.3.1hashtable概述
hash table可提供对任何有名项的存取操作和删除操作。由于操作对象是有名项,所以hash table也可以被视为一种字典结构。这种结构的用意在于提供常数时间之基本操作。
hash function(散列函数)的作用是将大数映射为小数,解决索引值过大的问题。但是用hash function时可能会有不同的元素被映射到相同位置,这是无法避免的,这便是所谓的“碰撞”问题。
解决碰撞问题的方法有:线性探测、二次探测、开链等做法。
线性探测(liner probing)
负载系数(laoding factor):意指元素个数除以表格大小,负载系数永远在0~1之间——除非采用开链策略。
当hash function计算某个元素的插入位置,而该位置上的空间已不在可用,那最简单的方法就是循环往下一一寻找,直到找到一个可用的空间,进行元素搜索操作时道理也是相同的,就不在赘述。至于元素删除,必须采用惰性删除,也就是只标记删除符号,实际删除操作则是等待表格重新整理时再进行——这是因为hash table中的而每一个元素不仅表述他自己,也关系到其他元素的排列。
下图是线性探测的一个实例。
分析线性探测的表现需要两个假设:(1)表格足够大;(2)每个元素都够独立。在这个假设下,最坏的情况是线性寻访整个表格,平均情况则是寻访一半表格,这和我们的期望相差甚远。
就以上面的例子而言:平均插入成本成长的幅度,远高于负载系数的成长幅度。这样的现象在hashing过程中被称为主集团。
二次探测(quadratic probing)
二次探测主要用来解决主集团的问题。其命名由来是因为解决碰撞问题的方程式是个二次方程。下图是二次探测的实例。
开链(separate chaining)
开链法是在每一个表格元素中维护一个list:hash function 为我们分配一个list,然后我们在那个list身上执行元素的插入、搜寻、删除等操作。
5.3.2hash table的桶子(buckets)与节点(nodes)
上图是以开链法完成hash table的图形描述,为了解说SGI STL源代码。遵循SGI命名,称hash table表格内的元素为桶子,此名称的大约意义是,表格内的每个单元,涵盖的不只是个节点,甚至可能是一桶节点。
5.3.3hash table的迭代器
hash table的迭代器必须永远维系着与整个“bukets vector”的关系,并记录目前所指的节点。注意该迭代器没有后退操作,也没有所谓逆向迭代器。
5.3.4hashtable的数据结构
buckets聚合体以vector完成,以利动态扩充。
省略很多很多,等我去吧哈希算法复习一遍再来搞这个。
6算法
6.1算法概观
算法,问题之解法也。
以有限步骤,解决逻辑或数学上的问题,这一专门科目我们称为算法。
广义上而言我们写的每一个每个程序都是一个算法,特定的算法一般搭配特定的数据结构,几乎可以说特定的数据结构是为了实现特定的算法。
6.1.1算法总览
下栏会议算法名称的字母顺序列表,表格之中凡是不在STL标准规格之列的SGI专属算法,都已*加以表示。
以下“质变”栏意思为“会改变其操作对象”
stl算法总览
6.1.2质变算法mutating algorithms——会改变操作对象的的值
所谓质变算法是指运算过程中会更改区间内(迭代器所指)的元素内容,诸如拷贝,互换,替换等算发都属于此类
6.1.3非质变算法nonmutating algorithms——不改变操作对象的值
所谓非质变算法是指运算过程中不会更改区间内(迭代器所指)的元素内容,诸如查找,匹配,计数等算法都属于此类。
6.1.4STL算法的一般形式
所有的泛型算法的前两个参数都是一对迭代器,通常称为first和last,用来标识算法的操作区间,这个操作区间的必要条件是,必须能够经由累加操作符的反复应用,从first到达last。
许多STL算法不只支持一个版本,创建多个版本可以更好的适用与更大的范围。
质变算法,通常提供两个版本,一个是in-place(就地进行)版,就地改变其操作对象:另一个版本是copy(另地进行),将操作对象的内容复制一份副本,然后再副本上进行修改并返回该副本。
所有的数值算法都实现于<stl_numeric.h>之中,这是内部文件,STL规定用户必须包含的是上层的<numeric>,其他STL算法都实现于SGL的<stl_algo.h>和<stl_algobase.h>头文件中,也是其内部文件:欲使用这些算法,必须包含上层头相关头文件<algorithm>.
6.2算法的泛化过程
如何将算法独立于其所处理的数据结构之外,不受数据结构的羁绊,或者说我们如何在即将处理未知的数据结构上,正确的实现所有操作了。
关键在于,只要把操作对象的型别加以抽象化,把操作对象的标示法和区间目标的移动行为抽象化,整个算法就都在一个抽象的层面工作了。整个过程称为算法的泛型化,简称泛化。
具体泛化例子请看书上294页,找不到可以省略写的地方。
6.3数值算法
使用他们必须包含头文件<numeric>。
6.3.1accumulate
//版本一
template<class InputIt, class T>
constexpr // C++20 起
T accumulate(InputIt first, InputIt last, T init)
{
for (; first != last; ++first) {
init = std::move(init) + *first; // C++20 起有 std::move
}
return init;
}
//版本二
template<class InputIt, class T, class BinaryOperation>
constexpr // C++20 起
T accumulate(InputIt first, InputIt last, T init,
BinaryOperation op)
{
for (; first != last; ++first) {
init = op(std::move(init), *first); // C++20 起有 std::move
}
return init;
}
计算给定值 init
与给定范围 [first, last)
中元素的和。第一版本用 operator+
,第二版本用二元函数 op求和元素。注意你一定得提供一个初始值,这样做是为了当区间为空时仍能获得一个明确的值。
参数
first, last | - | 要求和的元素范围 |
init | - | 和的初值 |
op | - | 被使用的二元函数对象。接收当前积累值 a (初始化为 init )和当前元素 b 的二元运算符。 |
返回值
1) 给定值与给定范围中的元素的和。
2) 给定范围在 op
上左折叠的结果
//示例
#include <iostream>
#include <vector>
#include <numeric>
#include <string>
#include <functional>
int main()
{
std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int sum = std::accumulate(v.begin(), v.end(), 0);
int product = std::accumulate(v.begin(), v.end(), 1, std::multiplies<int>());
auto dash_fold = [](std::string a, int b) {
return std::move(a) + '-' + std::to_string(b);
};
std::string s = std::accumulate(std::next(v.begin()), v.end(),
std::to_string(v[0]), // 用首元素开始
dash_fold);
// 使用逆向迭代器右折叠
std::string rs = std::accumulate(std::next(v.rbegin()), v.rend(),
std::to_string(v.back()), // 用首元素开始
dash_fold);
std::cout << "sum: " << sum << '\n'
<< "product: " << product << '\n'
<< "dash-separated string: " << s << '\n'
<< "dash-separated string (right-folded): " << rs << '\n';
}
sum: 55
product: 3628800
dash-separated string: 1-2-3-4-5-6-7-8-9-10
dash-separated string (right-folded): 10-9-8-7-6-5-4-3-2-1
6.3.2adjacent_difference
//版本一
template<class InputIt, class OutputIt>
constexpr // C++20 起
OutputIt adjacent_difference(InputIt first, InputIt last,
OutputIt d_first)
{
if (first == last) return d_first;
typedef typename std::iterator_traits<InputIt>::value_type value_t;
value_t acc = *first;
*d_first = acc;
while (++first != last) {
value_t val = *first;
*++d_first = val - std::move(acc); // C++20 起有 std::move
acc = std::move(val);
}
return ++d_first;
}
//版本二
template<class InputIt, class OutputIt, class BinaryOperation>
constexpr // C++20 起
OutputIt adjacent_difference(InputIt first, InputIt last,
OutputIt d_first, BinaryOperation op)
{
if (first == last) return d_first;
typedef typename std::iterator_traits<InputIt>::value_type value_t;
value_t acc = *first;
*d_first = acc;
while (++first != last) {
value_t val = *first;
*++d_first = op(val, std::move(acc)); // C++20 起有 std::move
acc = std::move(val);
}
return ++d_first;
}
计算 [first, last)
范围中每对相邻元素的第二个和第一个的差,并写入它们到始于 d_first + 1
的范围。写入不修改的 *first
副本到 *d_first
。
可以采用就地运算方式,就是让d_first等于first,这种情况下他是一个质变算法。
参数
first, last | - | 元素范围 |
d_first | - | 目标范围的起始 |
policy | - | 所用的执行策略。细节见执行策略。 |
op | - | 被使用的二元函数对象。 |
返回值
指向最后被写入元素后一位置的迭代器。
//示例
#include <numeric>
#include <vector>
#include <iostream>
#include <functional>
int main()
{
// 默认实现——二个项之间的差
std::vector<int> v{2, 4, 6, 8, 10, 12, 14, 16, 18, 20};
std::adjacent_difference(v.begin(), v.end(), v.begin());
for (auto n : v) {
std::cout << n << ' ';
}
std::cout << '\n';
// 斐波那契
// 注意,列表中下一项是当前迭代的结果
v = std::vector<int>(10);
v[0] = 1;
std::adjacent_difference(v.begin(), v.end() - 1, v.begin() + 1, std::plus<int>());
for (auto n : v) {
std::cout << n << ' ';
}
std::cout << '\n';
}
2 2 2 2 2 2 2 2 2 2
1 1 2 3 5 8 13 21 34 55
6.3.3inner_product
//版本一
template<class InputIt1, class InputIt2, class T>
constexpr // C++20 起
T inner_product(InputIt1 first1, InputIt1 last1,
InputIt2 first2, T init)
{
while (first1 != last1) {
init = std::move(init) + *first1 * *first2; // C++20 起有 std::move
++first1;
++first2;
}
return init;
}
//版本二
template<class InputIt1, class InputIt2,
class T,
class BinaryOperation1, class BinaryOperation2>
constexpr // C++20 起
T inner_product(InputIt1 first1, InputIt1 last1,
InputIt2 first2, T init,
BinaryOperation1 op1,
BinaryOperation2 op2)
{
while (first1 != last1) {
init = op1(std::move(init), op2(*first1, *first2)); // C++20 起有 std::move
++first1;
++first2;
}
return init;
}
计算内积(即积之和)或在范围 [first1, last1)
和始于 first2
的范围上进行有序映射/规约操作。注意一定得提供一个初始值init,这样做的原因是当区间为空时,依旧可以获得一个明确的值。
参数
first1, last1 | - | 元素范围 |
first2 | - | 第二个元素范围的起始 |
init | - | 积的和的初值 |
op1 | - | 被使用的二元函数对象。此“和”函数接收 op2 所返回的值和当前积累器的值,并产生存储于积累器的新值。 |
op2 | - | 被使用的二元函数对象。此“积”函数从每个范围接收一个值并产生新值。 |
返回值
上述 acc
的最终值。
//示例
#include <numeric>
#include <iostream>
#include <vector>
#include <functional>
int main()
{
std::vector<int> a{0, 1, 2, 3, 4};
std::vector<int> b{5, 4, 2, 3, 1};
int r1 = std::inner_product(a.begin(), a.end(), b.begin(), 0);
std::cout << "Inner product of a and b: " << r1 << '\n';
int r2 = std::inner_product(a.begin(), a.end(), b.begin(), 0,
std::plus<>(), std::equal_to<>());
std::cout << "Number of pairwise matches between a and b: " << r2 << '\n';
}
Inner product of a and b: 21
Number of pairwise matches between a and b: 2
6.3.4partial_sum
//版本一
template<class InputIt, class OutputIt>
constexpr // C++20 起
OutputIt partial_sum(InputIt first, InputIt last,
OutputIt d_first)
{
if (first == last) return d_first;
typename std::iterator_traits<InputIt>::value_type sum = *first;
*d_first = sum;
while (++first != last) {
sum = std::move(sum) + *first; // C++20 起有 std::move
*++d_first = sum;
}
return ++d_first;
// 或 C++14 起:
// return std::partial_sum(first, last, d_first, std::plus<>());
}
//版本二
template<class InputIt, class OutputIt, class BinaryOperation>
constexpr // C++20 起
OutputIt partial_sum(InputIt first, InputIt last,
OutputIt d_first, BinaryOperation op)
{
if (first == last) return d_first;
typename std::iterator_traits<InputIt>::value_type sum = *first;
*d_first = sum;
while (++first != last) {
sum = op(std::move(sum), *first); // C++20 起有 std::move
*++d_first = sum;
}
return ++d_first;
}
计算范围 [first, last)
的子范围中元素的部分和,并写入到始于 d_first
的范围。第一版本用 operator+
,第二版本用给定的二元函数 op
对元素求和。它会将*first赋值给*d_first,将*first和*(first+1)的和赋值给*(result+1),依次类推,注意d_first可以等于first,这种情况下他是一个质变算法。
参数
first, last | - | 要求和的元素范围 |
d_first | - | 目标范围起始;可以等于 first |
op | - | 被使用的二元函数对象。 |
返回值
指向最后被写入元素后一元素的迭代器。
//示例
#include <numeric>
#include <vector>
#include <iostream>
#include <iterator>
#include <functional>
int main()
{
std::vector<int> v = {2, 2, 2, 2, 2, 2, 2, 2, 2, 2}; // 或 std::vector<int>v(10, 2);
std::cout << "The first 10 even numbers are: ";
std::partial_sum(v.begin(), v.end(),
std::ostream_iterator<int>(std::cout, " "));
std::cout << '\n';
std::partial_sum(v.begin(), v.end(), v.begin(), std::multiplies<int>());
std::cout << "The first 10 powers of 2 are: ";
for (auto n : v) {
std::cout << n << " ";
}
std::cout << '\n';
}
The first 10 even numbers are: 2 4 6 8 10 12 14 16 18 20
The first 10 powers of 2 are: 2 4 8 16 32 64 128 256 512 1024
6.3.5power
这个算法由SGI专属,并不在STL标准之列,他用来计算某数的n幂次方,这里所谓的n幂次是指对自己进行某种运算,达n次,运算类型可由外界指定。
//版本一,乘幂
template <class T, class Integer>
inline T power(T x, Integer n) {
return power(x, n, multiplies<T>()); //指定运算形式为乘法
}
//版本二,幂次方,如果指定为乘法运算,则当n>=0时返回x^n
template <class T, class Integer, class MonoidOperation>
T power(T x, Integer n, MonoidOperation op) {
if (n == 0)
return identity_element(op); //取出“证同元素”identity element
else {
while ((n & 1) == 0) { //所谓证同元素,见第七章-
n >>= 1;
x = op(x, x);
}
T result = x;
n >>= 1;
while (n != 0) {
x = op(x, x);
if ((n & 1) != 0)
result = op(result, x);
n >>= 1;
}
return result;
}
}
6.3.6iota
这个算法由SGI专属,并不在STL标准之列,它用来设定某个区间的内容,使其内的每一个元素从指定的value值开始,呈现递增状态。
template<class ForwardIterator, class T>
constexpr // C++20 起
void iota(ForwardIterator first, ForwardIterator last, T value)
{
while(first != last) {
*first++ = value;
++value;
}
}
参数
first, last | - | 以 value 开始,按顺序递增填充的值的范围 |
value | - | 要存储的初始值,表达式 ++value 必须为良式 |
返回值
(无)
//示例
#include <algorithm>
#include <iostream>
#include <list>
#include <numeric>
#include <random>
#include <vector>
int main()
{
std::list<int> l(10);
std::iota(l.begin(), l.end(), -4);
std::vector<std::list<int>::iterator> v(l.size());
std::iota(v.begin(), v.end(), l.begin());
std::shuffle(v.begin(), v.end(), std::mt19937{std::random_device{}()});
std::cout << "Contents of the list: ";
for(auto n: l) std::cout << n << ' ';
std::cout << '\n';
std::cout << "Contents of the list, shuffled: ";
for(auto i: v) std::cout << *i << ' ';
std::cout << '\n';
}
Contents of the list: -4 -3 -2 -1 0 1 2 3 4 5
Contents of the list, shuffled: 0 -1 3 4 -4 1 -2 -3 2 5
6.4基本算法
6.4.1equal、fill、fill_n、iter_swap、lexicographical_compare、max、min、mismatch、swap
equal
如果两个序列在[first,last)区间内相等,equal返回true。如果第二序列元素更多多出来的元素不予考虑。要先判断两个序列元素个数是否相同,可以通过if判断或者使用容器提供的equality,第一版本缺省采用元素型别所提供的的equality操作符来进行大小比较,第二版本允许我们指定仿函数p作为比较依据。
//版本一
template<class InputIt1, class InputIt2>
bool equal(InputIt1 first1, InputIt1 last1,
InputIt2 first2)
{
for (; first1 != last1; ++first1, ++first2) {
if (!(*first1 == *first2)) {
return false;
}
}
return true;
}
//版本二
template<class InputIt1, class InputIt2, class BinaryPredicate>
bool equal(InputIt1 first1, InputIt1 last1,
InputIt2 first2, BinaryPredicate p)
{
for (; first1 != last1; ++first1, ++first2) {
if (!p(*first1, *first2)) {
return false;
}
}
return true;
}
参数
first1, last1 | - | 进行比较的第一个范围 |
first2, last2 | - | 进行比较的第二个范围 |
p | - | 若元素应被当做相等则返回 true 的二元谓词。 |
返回值
如果范围 [first1, last1)
的长度不等于范围 [first2, last2)
的长度则返回 false 。
如果两个范围内的元素都相等,则返回 true 。
否则返回 false 。
//下面的代码使用 equal() 来测试字符串是否是回文
#include <iostream>
#include <algorithm>
#include <string>
void test(const std::string& s)
{
if(std::equal(s.begin(), s.begin() + s.size()/2, s.rbegin())) {
std::cout << "\"" << s << "\" is a palindrome\n";
} else {
std::cout << "\"" << s << "\" is not a palindrome\n";
}
}
int main()
{
test("radar");
test("hello");
}
"radar" is a palindrome
"hello" is not a palindrome
fill
将区间内所有元素改填新值
template< class ForwardIt, class T >
void fill(ForwardIt first, ForwardIt last, const T& value)
{
for (; first != last; ++first) { //迭代走过整个区间
*first = value; //设定新值
}
}
参数
first, last | - | 要修改的元素范围 |
value | - | 要赋的值 |
policy | - | 所用的执行策略。细节见执行策略。 |
//下列代码用 fill() 设置 int 的 vector 的所有元素为 -1 :
#include <algorithm>
#include <vector>
#include <iostream>
int main()
{
std::vector<int> v{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
std::fill(v.begin(), v.end(), -1);
for (auto elem : v) {
std::cout << elem << " ";
}
std::cout << "\n";
}
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1
fill_n
将区间内的前n个元素改填为新值,返回的迭代器指向被填入的最后一个元素下一个位置
template<class OutputIt, class Size, class T>
OutputIt fill_n(OutputIt first, Size count, const T& value)
{
for (Size i = 0; i < count; ++i) {
*first++ = value;
}
return first;
}
注意n不能大于容器的大小,因为每次迭代进行的是assignment操作,是一种覆写操作,所以一旦超出容器范围,会造成不可知的后果。
参数
first | - | 要修改的元素范围起始 |
count | - | 要修改的元素数 |
value | - | 要赋的值 |
policy | - | 所用的执行策略。细节见执行策略。 |
返回值
(无) | (C++11 前) |
若 | (C++11 起) |
//下列代码用 fill_n() 赋值 -1 给 int 的 vector 的前一半
#include <algorithm>
#include <vector>
#include <iostream>
#include <iterator>
int main()
{
std::vector<int> v1{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
std::fill_n(v1.begin(), 5, -1);
std::copy(begin(v1), end(v1), std::ostream_iterator<int>(std::cout, " "));
std::cout << "\n";
}
-1 -1 -1 -1 -1 5 6 7 8 9
iter_swap
交换给定的迭代器所指向的元素的值。
template<class ForwardIt1, class ForwardIt2>
constexpr void iter_swap(ForwardIt1 a, ForwardIt2 b) // C++20 起为 constexpr
{
using std::swap;
swap(*a, *b);
}
参数
a, b | - | 指向要交换的元素的迭代器 |
//下面是选择排序在 C++ 中的实现
#include <random>
#include <vector>
#include <iostream>
#include <algorithm>
#include <functional>
template<class ForwardIt>
void selection_sort(ForwardIt begin, ForwardIt end)
{
for (ForwardIt i = begin; i != end; ++i)
std::iter_swap(i, std::min_element(i, end));
}
int main()
{
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dist(-10, 10);
std::vector<int> v;
std::generate_n(back_inserter(v), 20, bind(dist, gen));
std::cout << "Before sort: ";
for(auto e : v) std::cout << e << " ";
selection_sort(v.begin(), v.end());
std::cout << "\nAfter sort: ";
for(auto e : v) std::cout << e << " ";
std::cout << '\n';
}
Before sort: -7 6 2 4 -1 6 -9 -1 2 -5 10 -9 -5 -3 -5 -3 6 6 1 8
After sort: -9 -9 -7 -5 -5 -5 -3 -3 -1 -1 1 2 2 4 6 6 6 6 8 10
lexicographical_compare
以“字典排列方式”对两个序列进行比较,比较操作针对两序列中对应位置上的元素进行,并持续到1、某一组对应元素彼此不相等:2、同时到达last1和last2;3、到达lsat1或last2。
字典序比较是拥有下列属性的操作:
- 逐元素比较二个范围。
- 首个不匹配元素定义范围是否按字典序小于或大于另一个。
- 若一个范围是另一个的前缀,则较短的范围小于另一个。
- 若二个范围拥有等价元素和相同长度,则范围按字典序相等。
- 空范围按字典序小于任何非空范围。
- 二个空范围按字典序相等。
当这个函数在对应位置上发现第一组不相等的元素时,有下列几种可能:
- 如果第一序列元素较小,返回true,否则返回false
- 如果到达last1而尚未到达last2,返回true
- 如果到达last2而尚未到达last1,返回false
- 如果同时到达last1和last2,返回false
也就是说第一序列以字典排列方式而言不小于第二序列
//版本一
template<class InputIt1, class InputIt2>
bool lexicographical_compare(InputIt1 first1, InputIt1 last1,
InputIt2 first2, InputIt2 last2)
{
for ( ; (first1 != last1) && (first2 != last2); ++first1, (void) ++first2 ) {
if (*first1 < *first2) return true;//第一系列元素值小于第二序列元素值
if (*first2 < *first1) return false;//第二序列元素值小于第一序列元素值
}
return (first1 == last1) && (first2 != last2);//如果第一序列到达结尾,而第二序列尚有余
//额,那么第一序列小于第二序列
}
//版本二
template<class InputIt1, class InputIt2, class Compare>
bool lexicographical_compare(InputIt1 first1, InputIt1 last1,
InputIt2 first2, InputIt2 last2,
Compare comp)
{
for ( ; (first1 != last1) && (first2 != last2); ++first1, (void) ++first2 ) {
if (comp(*first1, *first2)) return true;
if (comp(*first2, *first1)) return false;
}
return (first1 == last1) && (first2 != last2);
}
参数
first1, last1 | - | 要检验的第一个元素范围 |
first2, last2 | - | 要检验的第二个元素范围 |
policy | - | 所用的执行策略。细节见执行策略。 |
comp | - | 比较函数对象(即满足比较 (Compare) 要求的对象),若首个参数小于第二个,则返回 true |
返回值
若第一范围按字典序小于第二个则为 true 。
//示例
#include <algorithm>
#include <iostream>
#include <vector>
#include <random>
int main()
{
std::vector<char> v1 {'a', 'b', 'c', 'd'};
std::vector<char> v2 {'a', 'b', 'c', 'd'};
std::mt19937 g{std::random_device{}()};
while (!std::lexicographical_compare(v1.begin(), v1.end(),
v2.begin(), v2.end())) {
for (auto c : v1) std::cout << c << ' ';
std::cout << ">= ";
for (auto c : v2) std::cout << c << ' ';
std::cout << '\n';
std::shuffle(v1.begin(), v1.end(), g);
std::shuffle(v2.begin(), v2.end(), g);
}
for (auto c : v1) std::cout << c << ' ';
std::cout << "< ";
for (auto c : v2) std::cout << c << ' ';
std::cout << '\n';
}
a b c d >= a b c d
d a b c >= c b d a
b d a c >= a d c b
a c d b < c d a b
max
返回给定值中的较大者。有两个版本,版本一使用对象使用对象型别T所提供的greater-than操作符来判断大小,版本二使用仿函数comp来判断大小。
//版本一
template<class T>
const T& max(const T& a, const T& b)
{
return (a < b) ? b : a;
}
//版本二
template<class T, class Compare>
const T& max(const T& a, const T& b, Compare comp)
{
return (comp(a, b)) ? b : a;
}
参数
a, b | - | 要比较的值 |
ilist | - | 拥有要比较的值的 initializer_list |
cmp | - | 比较函数对象(即满足比较 (Compare) 要求的对象),若若 a 小于 b ,则返回 true 。 |
返回值
1-2) a
与 b
的较大者。若它们等价,则返回 a
。
3-4) ilist
中的最大值。若有数个等价于最大者的值,则返回最左侧的这种值。
#include <algorithm>
#include <iostream>
#include <string>
int main()
{
std::cout << "larger of 1 and 9999: " << std::max(1, 9999) << '\n'
<< "larger of 'a', and 'b': " << std::max('a', 'b') << '\n'
<< "longest of \"foo\", \"bar\", and \"hello\": " <<
std::max( { "foo", "bar", "hello" },
[](const std::string& s1, const std::string& s2) {
return s1.size() < s2.size();
}) << '\n';
}
larger of 1 and 9999: 9999
larger of 'a', and 'b': b
longest of "foo", "bar", and "hello": hello
min
返回给定值中的较小者。有两个版本,版本一使用对象使用对象型别T所提供的greater-than操作符来判断大小,版本二使用仿函数comp来判断大小。
其形式和max近乎一样。
mismatch
用来平行比较两个序列,指出两者之间的第一个不匹配的点,返回一对迭代器分别指向两序列中不匹配的点,如果都匹配返回的是两者的last迭代器。
//版本一
template<class InputIt1, class InputIt2>
std::pair<InputIt1, InputIt2>
mismatch(InputIt1 first1, InputIt1 last1, InputIt2 first2)
{
while (first1 != last1 && *first1 == *first2) {
++first1, ++first2;
}
return std::make_pair(first1, first2);
}
//版本二
template<class InputIt1, class InputIt2, class BinaryPredicate>
std::pair<InputIt1, InputIt2>
mismatch(InputIt1 first1, InputIt1 last1, InputIt2 first2, BinaryPredicate p)
{
while (first1 != last1 && p(*first1, *first2)) {
++first1, ++first2;
}
return std::make_pair(first1, first2);
}
参数
first1, last1 | - | 第一元素范围 |
first2, last2 | - | 第二元素范围 |
policy | - | 所用的执行策略。细节见执行策略。 |
p | - | 若元素应被当做相等则返回 true 的二元谓词。 |
返回值
拥有指向首二个不相等元素的迭代器的 std::pair 。
若比较抵达 | (C++14 前) |
若比较抵达 | (C++14 起) |
//此程序确定同时在给定字符串起始与在其结尾按逆序同时找到的最长子串(可能重叠)。
#include <iostream>
#include <string>
#include <algorithm>
std::string mirror_ends(const std::string& in)
{
return std::string(in.begin(),
std::mismatch(in.begin(), in.end(), in.rbegin()).first);
}
int main()
{
std::cout << mirror_ends("abXYZba") << '\n'
<< mirror_ends("abca") << '\n'
<< mirror_ends("aba") << '\n';
}
ab
a
aba
swap
该函数用来交换两个对象的内容。
template<class T>
inline void swap(T& a,T& b){
T tmp=a;
a=b;
b=tmp;
}
6.4.2copy——强化效率无所不其极
copy是一个被常常调用的函数,如果能够使用内存直接复制行为,便能够省下大量时间,为此SGI STL的copy算法用尽各种办法,包括函数重载、型别特性偏特化、等编程技巧,下图表示copy的操作脉络。
复制 [first, last)
所定义的范围中的元素到始于 d_first
的另一范围。也就是说,它会执行赋值操作*d_first=*first……等等,返回一个迭代器:d_first+(last-first)、copy对其template参数所要求的条件非常宽松,其输入区间只要inputlterators,输出区间只要outputlterator。意味可以使用copy算法,将容器的任何一段区间的内容,复制到任何容器的任何一段区间上。
//版本一
template<class InputIt, class OutputIt>
OutputIt copy(InputIt first, InputIt last,
OutputIt d_first)
{
while (first != last) {
*d_first++ = *first++;
}
return d_first;
}
//版本二
template<class InputIt, class OutputIt, class UnaryPredicate>
OutputIt copy_if(InputIt first, InputIt last,
OutputIt d_first, UnaryPredicate pred)
{
while (first != last) {
if (pred(*first))
*d_first++ = *first;
++first;
}
return d_first;
}
参数
first, last | - | 要复制的元素范围 |
d_first | - | 目标范围的起始。 |
policy | - | 所用的执行策略。细节见执行策略。 |
pred | - | 对所要求的元素则返回 true 的一元谓词。 |
返回值
指向目标范围中最后复制元素的下个元素的输出迭代器。
//下列代码用 copy 复制一个 vector 的内容到另一个,并显示结果 vector :
#include <algorithm>
#include <iostream>
#include <vector>
#include <iterator>
#include <numeric>
int main()
{
std::vector<int> from_vector(10);
std::iota(from_vector.begin(), from_vector.end(), 0);
std::vector<int> to_vector;
std::copy(from_vector.begin(), from_vector.end(),
std::back_inserter(to_vector));
// 或可选地,
// std::vector<int> to_vector(from_vector.size());
// std::copy(from_vector.begin(), from_vector.end(), to_vector.begin());
// 任一方式都等价于
// std::vector<int> to_vector = from_vector;
std::cout << "to_vector contains: ";
std::copy(to_vector.begin(), to_vector.end(),
std::ostream_iterator<int>(std::cout, " "));
std::cout << '\n';
std::cout << "odd numbers in to_vector are: ";
std::copy_if(to_vector.begin(), to_vector.end(),
std::ostream_iterator<int>(std::cout, " "),
[](int x) { return (x % 2) == 1; });
std::cout << '\n';
}
to_vector contains: 0 1 2 3 4 5 6 7 8 9
odd numbers in to_vector are: 1 3 5 7 9
6.4.3copy_backward
template< class BidirIt1, class BidirIt2 >
BidirIt2 copy_backward(BidirIt1 first, BidirIt1 last, BidirIt2 d_last)
{
while (first != last) {
*(--d_last) = *(--last);
}
return d_last;
}
复制来自 [first, last)
所定义范围的元素,到终于 d_last
的范围。以逆序复制元素(首先复制末元素),但保持其相对顺序。
若 d_last
在 (first, last]
中则行为未定义。该情况下必须用 std::copy 取代 std::copy_backward。
参数
first, last | - | 要复制的元素范围 |
d_last | - | 目标范围的结尾。 |
返回值
指向最后复制元素的迭代器。
#include <algorithm>
#include <iostream>
#include <vector>
int main()
{
std::vector<int> from_vector;
for (int i = 0; i < 10; i++) {
from_vector.push_back(i);
}
std::vector<int> to_vector(15);
std::copy_backward(from_vector.begin(), from_vector.end(), to_vector.end());
std::cout << "to_vector contains: ";
for (auto i: to_vector) {
std::cout << i << " ";
}
}
to_vector contains: 0 0 0 0 0 0 1 2 3 4 5 6 7 8 9
6.5set相关算法
STL一共提供了四种与set相关的算法,别是并集、交集、差集、对称差集。本书的四个算法所接受的set,必须是有序区间,元素值得重复出现,本书的四个算法都至少有四个参数,分别表现为两个set区间。
6.5.1set_union
功能描述:求两个集合的并集
函数原型: set_union(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);
//求两个集合的并集
//注意:两个集合必须是有序序列
//beg1 容器1开始迭代器
//end1 容器1结束迭代器
//beg2 容器2开始迭代器
//end2 容器2结束迭代器
//dest 目标容器开始迭代器
由于s1和s2内的每一个元素都不需要唯一,因此如果某个值在s1出现n次,在s2出现m次,那么该值在输出区间中会出现max(m,n)次,其中n个来自于s1,其余来自s2。在STL set容器中m和n小于等于1;
struct set_union_fn {
template< std::input_iterator I1, std::sentinel_for<I1> S1,
std::input_iterator I2, std::sentinel_for<I2> S2,
std::weakly_incrementable O, class Comp = ranges::less,
class Proj1 = std::identity, class Proj2 = std::identity >
requires std::mergeable<I1, I2, O, Comp, Proj1, Proj2>
constexpr ranges::set_union_result<I1, I2, O>
operator()( I1 first1, S1 last1, I2 first2, S2 last2,
O result, Comp comp = {},
Proj1 proj1 = {}, Proj2 proj2 = {} ) const {
for (; !(first1 == last1 or first2 == last2); ++result) {
if (std::invoke(comp, std::invoke(proj1, *first1),
std::invoke(proj2, *first2))) {
*result = *first1; ++first1;
}
else if (std::invoke(comp, std::invoke(proj2, *first2),
std::invoke(proj1, *first1))) {
*result = *first2; ++first2;
}
else {
*result = *first1; ++first1; ++first2;
}
}
auto res1 = ranges::copy(std::move(first1), std::move(last1), std::move(result));
auto res2 = ranges::copy(std::move(first2), std::move(last2), std::move(res1.out));
return {std::move(res1.in), std::move(res2.in), std::move(res2.out)};
}
template< ranges::input_range R1, ranges::input_range R2,
std::weakly_incrementable O, class Comp = ranges::less,
class Proj1 = std::identity, class Proj2 = std::identity >
requires std::mergeable<ranges::iterator_t<R1>, ranges::iterator_t<R2>,
O, Comp, Proj1, Proj2>
constexpr ranges::set_union_result<ranges::borrowed_iterator_t<R1>,
ranges::borrowed_iterator_t<R2>, O>
operator()( R1&& r1, R2&& r2, O result, Comp comp = {},
Proj1 proj1 = {}, Proj2 proj2 = {} ) const {
return (*this)(ranges::begin(r1), ranges::end(r1),
ranges::begin(r2), ranges::end(r2),
std::move(result), std::move(comp),
std::move(proj1), std::move(proj2));
}
};
inline constexpr set_union_fn set_union{};
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
void print(const auto& in1, const auto& in2, auto first, auto last) {
std::cout << "{ ";
for (const auto& e : in1) { std::cout << e << ' '; }
std::cout << "} ∪ { ";
for (const auto& e : in2) { std::cout << e << ' '; }
std::cout << "} =\n{ ";
while (!(first == last)) { std::cout << *first++ << ' '; }
std::cout << "}\n\n";
}
int main()
{
std::vector<int> in1, in2, out;
in1 = {1, 2, 3, 4, 5};
in2 = { 3, 4, 5, 6, 7};
out.resize(in1.size() + in2.size());
const auto ret = std::ranges::set_union(in1, in2, out.begin());
print(in1, in2, out.begin(), ret.out);
in1 = {1, 2, 3, 4, 5, 5, 5};
in2 = { 3, 4, 5, 6, 7};
out.clear();
out.reserve(in1.size() + in2.size());
std::ranges::set_union(in1, in2, std::back_inserter(out));
print(in1, in2, out.cbegin(), out.cend());
}
{ 1 2 3 4 5 } ∪ { 3 4 5 6 7 } =
{ 1 2 3 4 5 6 7 }
{ 1 2 3 4 5 5 5 } ∪ { 3 4 5 6 7 } =
{ 1 2 3 4 5 5 5 6 7 }
6.5.2set_intersection
功能描述:求两个容器的交集
函数原型: set_intersection(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);
//求两个容器的交集
//注意:两个集合必须是有序序列
//beg1 容器1开始迭代器
//end1 容器1结束迭代器
//beg2 容器2开始迭代器
//end2 容器2结束迭代器
//dest 目标容器开始迭代器
struct set_intersection_fn {
template< std::input_iterator I1, std::sentinel_for<I1> S1,
std::input_iterator I2, std::sentinel_for<I2> S2,
std::weakly_incrementable O, class Comp = ranges::less,
class Proj1 = std::identity, class Proj2 = std::identity >
requires std::mergeable<I1, I2, O, Comp, Proj1, Proj2>
constexpr ranges::set_union_result<I1, I2, O>
operator()( I1 first1, S1 last1, I2 first2, S2 last2,
O result, Comp comp = {},
Proj1 proj1 = {}, Proj2 proj2 = {} ) const {
while (!(first1 == last1 or first2 == last2)) {
if (std::invoke(comp, std::invoke(proj1, *first1), std::invoke(proj2, *first2)))
++first1;
else if (std::invoke(comp, std::invoke(proj2, *first2), std::invoke(proj1, *first1)))
++first2;
else
*result = *first1, ++first1, ++first2, ++result;
}
return {ranges::next(std::move(first1), std::move(last1)),
ranges::next(std::move(first2), std::move(last2)),
std::move(result)};
}
template< ranges::input_range R1, ranges::input_range R2,
std::weakly_incrementable O, class Comp = ranges::less,
class Proj1 = std::identity, class Proj2 = std::identity >
requires std::mergeable<ranges::iterator_t<R1>, ranges::iterator_t<R2>,
O, Comp, Proj1, Proj2>
constexpr ranges::set_intersection_result<ranges::borrowed_iterator_t<R1>,
ranges::borrowed_iterator_t<R2>, O>
operator()( R1&& r1, R2&& r2, O result, Comp comp = {},
Proj1 proj1 = {}, Proj2 proj2 = {} ) const {
return (*this)(ranges::begin(r1), ranges::end(r1),
ranges::begin(r2), ranges::end(r2),
std::move(result), std::move(comp),
std::move(proj1), std::move(proj2));
}
};
inline constexpr set_intersection_fn set_intersection{};
参数
first1, last1 | - | 迭代器-哨位对,代表输入的第一个已排序的范围 |
first2, last2 | - | 迭代器-哨位对,代表输入的第二个已排序的范围 |
r1 | - | 第一个已排序的范围 |
r2 | - | 第二个已排序的范围 |
result | - | 输出范围的起始 |
comp | - | 对投影后的元素使用的比较 |
proj1 | - | 对第一个范围的元素使用的投影 |
proj2 | - | 对第二个范围的元素使用的投影 |
返回值
{last1, last2, result_last} ,其中 result_last 是被构造的范围的终点。
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
void print(const auto& v, const auto& rem) {
std::cout << "{ ";
for (const auto& e : v)
std::cout << e << ' ';
std::cout << "}" << rem;
}
int main()
{
const auto in1 = {1, 2, 2, 3, 4, 5, 6 };
const auto in2 = {2, 2, 3, 3, 5, 7};
std::vector<int> out;
std::ranges::set_intersection(in1, in2, std::back_inserter(out));
print(in1, " ∩ "), print(in2, " = "), print(out, "\n");
}
{ 1 2 2 3 4 5 6 } ∩ { 2 2 3 3 5 7 } = { 2 2 3 5 }
6.5.3set_difference
功能描述:求两个集合的差集
函数原型: set_difference(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);
//求两个集合的差集
//注意:两个集合必须是有序序列
//beg1 容器1开始迭代器
//end1 容器1结束迭代器
//beg2 容器2开始迭代器
//end2 容器2结束迭代器
//dest 目标容器开始迭代器
struct set_difference_fn {
template< std::input_iterator I1, std::sentinel_for<I1> S1,
std::input_iterator I2, std::sentinel_for<I2> S2,
std::weakly_incrementable O, class Comp = ranges::less,
class Proj1 = std::identity, class Proj2 = std::identity >
requires std::mergeable<I1, I2, O, Comp, Proj1, Proj2>
constexpr ranges::set_difference_result<I1, O>
operator()( I1 first1, S1 last1, I2 first2, S2 last2,
O result, Comp comp = {},
Proj1 proj1 = {}, Proj2 proj2 = {} ) const {
while (!(first1 == last1 or first2 == last2)) {
if (std::invoke(comp, std::invoke(proj1, *first1),
std::invoke(proj2, *first2))) {
*result = *first1; ++first1; ++result;
}
else if (std::invoke(comp, std::invoke(proj2, *first2),
std::invoke(proj1, *first1))) {
++first2;
}
else {
++first1; ++first2;
}
}
return ranges::copy(std::move(first1), std::move(last1), std::move(result));
}
template< ranges::input_range R1, ranges::input_range R2,
std::weakly_incrementable O, class Comp = ranges::less,
class Proj1 = std::identity, class Proj2 = std::identity >
requires std::mergeable<ranges::iterator_t<R1>, ranges::iterator_t<R2>,
O, Comp, Proj1, Proj2>
constexpr ranges::set_difference_result<
ranges::borrowed_iterator_t<R1>, O>
operator()( R1&& r1, R2&& r2, O result, Comp comp = {},
Proj1 proj1 = {}, Proj2 proj2 = {} ) const {
return (*this)(ranges::begin(r1), ranges::end(r1),
ranges::begin(r2), ranges::end(r2),
std::move(result), std::move(comp),
std::move(proj1), std::move(proj2));
}
};
inline constexpr set_difference_fn set_difference{};
参数
first1, last1 | - | 第一已排序输入范围 |
first2, last2 | - | 第二已排序输入范围 |
r1 | - | 第一已排序输入范围 |
r2 | - | 第二已排序输入范围 |
result | - | 目标范围的起始 |
comp | - | 应用到投影后元素的比较器 |
proj1 | - | 应用到第一范围元素的投影 |
proj2 | - | 应用到第二范围元素的投影 |
返回值
{last1, result_last} ,其中 result_last 为被构造范围末尾。
#include <algorithm>
#include <cassert>
#include <iostream>
#include <iterator>
#include <string_view>
#include <vector>
auto print = [](const auto& v, std::string_view end = "") {
for (std::cout << "{ "; auto i : v) std::cout << i << ' ';
std::cout << "} " << end;
};
struct Order // 拥有非常有趣的数据的类
{
int order_id;
friend std::ostream& operator<<(std::ostream& os, const Order& ord) {
return os << "{" << ord.order_id << "},";
}
};
int main()
{
const auto v1 = {1, 2, 5, 5, 5, 9};
const auto v2 = {2, 5, 7};
std::vector<int> diff;
std::ranges::set_difference(v1, v2, std::back_inserter(diff));
print(v1, "∖ ");
print(v2, "= ");
print(diff, "\n");
// 我们想知道哪些顺序在旧和新状态之间“改变了”:
const std::vector<Order> old_orders { {1}, {2}, {5}, {9}, };
const std::vector<Order> new_orders { {2}, {5}, {7}, };
std::vector<Order> cut_orders(old_orders.size() + new_orders.size());
auto [old_orders_end, cut_orders_last] =
std::ranges::set_difference(old_orders, new_orders,
cut_orders.begin(), {},
&Order::order_id, &Order::order_id);
assert(old_orders_end == old_orders.end());
std::cout << "old orders = "; print(old_orders, "\n");
std::cout << "new orders = "; print(new_orders, "\n");
std::cout << "cut orders = "; print(cut_orders, "\n");
cut_orders.erase(cut_orders_last, end(cut_orders));
std::cout << "cut orders = "; print(cut_orders, "\n");
}
{ 1 2 5 5 5 9 } ∖ { 2 5 7 } = { 1 5 5 9 }
old orders = { {1}, {2}, {5}, {9}, }
new orders = { {2}, {5}, {7}, }
cut orders = { {1}, {9}, {0}, {0}, {0}, {0}, {0}, }
cut orders = { {1}, {9}, }
6.5.4set_symmetric_difference
计算二个已排序范围的对称差:赋值在一个但非两个范围中找到的元素到始于 result
的范围。结果范围亦已排序。此集合内含“出现于s1但不出现于s2”以及“出现于s2但不出现于s1”的每一个元素。
struct set_symmetric_difference_fn {
template< std::input_iterator I1, std::sentinel_for<I1> S1,
std::input_iterator I2, std::sentinel_for<I2> S2,
std::weakly_incrementable O, class Comp = ranges::less,
class Proj1 = std::identity, class Proj2 = std::identity >
requires std::mergeable<I1, I2, O, Comp, Proj1, Proj2>
constexpr ranges::set_symmetric_difference_result<I1, I2, O>
operator()( I1 first1, S1 last1, I2 first2, S2 last2, O result, Comp comp = {},
Proj1 proj1 = {}, Proj2 proj2 = {} ) const {
while (!(first1 == last1 or first2 == last2)) {
if (std::invoke(comp, std::invoke(proj1, *first1),
std::invoke(proj2, *first2))) {
*result = *first1; ++first1; ++result;
}
else if (std::invoke(comp, std::invoke(proj2, *first2),
std::invoke(proj1, *first1))) {
*result = *first2; ++first2; ++result;
}
else {
++first1; ++first2;
}
}
auto res1 {ranges::copy(std::move(first1), std::move(last1), std::move(result))};
auto res2 {ranges::copy(std::move(first2), std::move(last2), std::move(res1.out))};
return {std::move(res1.in), std::move(res2.in), std::move(res2.out)};
}
template< ranges::input_range R1, ranges::input_range R2,
std::weakly_incrementable O, class Comp = ranges::less,
class Proj1 = std::identity, class Proj2 = std::identity >
requires std::mergeable<ranges::iterator_t<R1>, ranges::iterator_t<R2>,
O, Comp, Proj1, Proj2>
constexpr ranges::set_symmetric_difference_result<
ranges::borrowed_iterator_t<R1>, ranges::borrowed_iterator_t<R2>, O>
operator()( R1&& r1, R2&& r2, O result, Comp comp = {},
Proj1 proj1 = {}, Proj2 proj2 = {} ) const {
return (*this)(ranges::begin(r1), ranges::end(r1),
ranges::begin(r2), ranges::end(r2),
std::move(result), std::move(comp),
std::move(proj1), std::move(proj2));
}
};
inline constexpr set_symmetric_difference_fn set_symmetric_difference{};
参数
first1, last1 | - | 第一已排序输入范围 |
first2, last2 | - | 第二已排序输入范围 |
r1 | - | 第一已排序输入范围 |
r2 | - | 第二已排序输入范围 |
result | - | 目标范围的起始 |
comp | - | 应用到投影后元素的比较器 |
proj1 | - | 应用到第一范围元素的投影 |
proj2 | - | 应用到第二范围元素的投影 |
返回值
{last1, result_last} ,其中 result_last 为被构造范围末尾。
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
void visualize_this(const auto& v, int min = 1, int max = 9) {
for (auto i{min}; i <= max; ++i) {
std::ranges::binary_search(v, i) ? std::cout << i : std::cout << '.';
std::cout << ' ';
}
std::cout << '\n';
}
int main()
{
const auto in1 = {1, 3, 4, 6, 7, 9};
const auto in2 = {1, 4, 5, 6, 9};
std::vector<int> out;
std::ranges::set_symmetric_difference(in1, in2, std::back_inserter(out));
visualize_this(in1);
visualize_this(in2);
visualize_this(out);
}
1 . 3 4 . 6 7 . 9
1 . . 4 5 6 . . 9
. . 3 . 5 . 7 . .
6.6其他算法
这部分内容多而细感觉列举出来,后面也不便查找,所以暂时搁置,要用时直接查找书本338至343页。
6.7常用算法简单列举
常用遍历算法
for_each //遍历容器
transform //搬运容器到另一个容器中
for_each
功能描述:实现遍历容器
函数原型: for_each(iterator beg, iterator end, _func);
//遍历算法 遍历容器元素
//beg 开始迭代器
//end 结束迭代器
//_func 函数或者函数对象
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
//常用遍历算法 for_each
//普通函数
void print01(int val) {
cout << val << " ";
}
//仿函数
class print02 {
public:
void operator()(int val) {
cout << val << " ";
}
};
void test01() {
vector<int>v;
for (int i = 0; i < 10; i++) {
v.push_back(i);
}
for_each(v.begin(), v.end(), print01);
cout << endl;
for_each(v.begin(), v.end(), print02());
cout << endl;
}
int main() {
test01();
return 0;
}
transform
功能描述:搬运容器到另一个容器中
函数原型: transform(iterator beg1, iterator end1, iterator beg2, _func);
//beg1 源容器开始迭代器
//end1 源容器结束迭代器
//beg2 目标容器开始迭代器
//_func 函数或者函数对象
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
//常用遍历算法 transform
class Transform {
public:
int operator()(int v) {
return v + 100;
}
};
class Myprint {
public:
void operator()(int val) {
cout << val << " ";
}
};
void test01(){
vector<int> v;
for (int i = 0; i < 10; i++) {
v.push_back(i);
}
vector<int> vTarget;//目标容器
vTarget.resize(v.size()); //目标容器需要提前开辟空间
transform(v.begin(), v.end(), vTarget.begin(), Transform());
for_each(vTarget.begin(), vTarget.end(), Myprint());
cout << endl;
}
int main() {
test01();
return 0;
}
stl常用查找算法
find //查找元素
find_if //按条件查找元素
adjacent_find //查找相邻重复元素
binary_search //二分查找法
count //统计元素个数
count_if //按条件统计元素个数
find
功能描述:查找指定元素,找到返回指定元素的迭代器,找不到返回结束 迭代器end()
函数原型: find(iterator beg, iterator end, value);
//按值查找元素,找到返回指定位置迭代器,找不到返回结束迭代器位置
//beg 开始迭代器
//end 结束迭代器
//value 查找的元素
vector<int> v;
for (int i = 0; i < 10;i++)
{
v.push_back(i);
}
//查找容器中是否有9这个元素
vector<int>::iterator it = find(v.begin(), v.end(), 9);
if(it == v.end())
{
cout << "not found " << endl;
}
else
{
cout << " found: " << *it << endl;
}
find_if
功能描述:按条件查找元素
函数原型: find_if(iterator beg, iterator end, _Pred);
//按值查找元素,返回指定位置迭代器,找不到返回结束迭代器位置
//beg 开始迭代器
//end 结束迭代器
// _Pred 函数或者谓词(返回bool类型的仿函数)
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<string>
//常用查找算法
//find_if
class GreaterFive
{
public:
bool operator()(int val)
{
return val == 5;
}
};
void test01(){
vector<int> v;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
//查找容器中是否有5这个元素
vector<int>::iterator it = find_if(v.begin(), v.end(), GreaterFive());
if (it == v.end())
{
cout << "not found " << endl;
}
else
{
cout << " found: " << *it << endl;
}
}
int main() {
test01();
return 0;
}
adjacent_find
功能描述:查找相邻重复元素
函数原型: adjacent_find(iterator beg, iterator end);
//查找相邻重复元素,返回相邻元素的第一个位置的迭代器
//beg 开始迭代器
//end 结束迭代器
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
//常用查找算法
//adjacent_find
void test01()
{
vector<int> v={0,2,2,1,3,4,4};
vector<int>::iterator pos = adjacent_find(v.begin(), v.end());
if (pos == v.end())
{
cout << "not found " << endl;
}
else
{
cout << "found : " << *pos << endl;
}
}
int main() {
test01();
return 0;
}
binary_search
功能描述:查找指定元素是否存在
函数原型: bool binary_search(iterator beg, iterator end, value);
//查找指定的元素, 查到 返回true 否则false
//注意:在无序序列中不可用
//beg 开始迭代器
//end 结束迭代器
//value 查找的元素
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
//常用查找算法 binary_search
void test01()
{
vector<int> v;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
// v.push_back(2);//如果是无序序列,结果未知
//查找容器中是否有9这个元素
//注意:容器必须是有序的序列
bool ret = binary_search(v.begin(), v.end(), 9);
if (ret)
{
cout << "found" << endl;
}
else
{
cout << "not found" << endl;
}
}
int main() {
test01();
return 0;
}
count
功能描述:统计元素个数
函数原型: count(iterator beg,iterator end, value);
//统计元素出现次数
//beg 开始迭代器
//end 结束迭代器
//value 统计的元素
vector<int> v;
v.push_back(10);
v.push_back(40);
v.push_back(20);
v.push_back(40);
v.push_back(20);
v.push_back(40);
//统计容器中40元素有几个
int num = count(v.begin(), v.end(), 20);
cout << "40's num is :" << num << endl;
count_if
功能描述:按条件统计元素个数
函数原型: count_if(iterator beg, iterator end, _Pred);
//按条件统计元素出现次数
//beg 开始迭代器
//end 结束迭代器
//_Pred 谓词
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<string>
//常用查找算法
//count_if
class Greater10 {
public:
bool operator()(int val) {
return val > 10;
}
};
int main() {
vector<int> v;
v.push_back(10);
v.push_back(40);
v.push_back(30);
v.push_back(40);
v.push_back(20);
v.push_back(40);
//统计容器中大于10元素有几个
int num = count_if(v.begin(), v.end(), Greater10());
cout << "greater than 10's num is :" << num << endl;
return 0;
}
stl常用排序算法
sort //对容器内元素进行排序
random_shuffle //洗牌 指定范围内的元素随机调整次序
merge //容器元素合并,并存储到另一容器中
reverse //反转指定范围的元素
sort
功能描述:对容器内元素进行排序
函数原型: sort(iterator beg, iterator end, _Pred);
//按值查找元素,找到返回指定位置迭代器,找不到返回结束迭代器位置
//beg 开始迭代器
//end 结束迭代器
//_Pred 谓词
#include<iostream>
using namespace std;
#include<algorithm>
#include<vector>
#include<functional>
//常用排序算法 sort
void myPrint(int val)
{
cout << val << " ";
}
void test01()
{
vector<int> v={2,5,1,3,4,6};
//利用sort进行升序
sort(v.begin(), v.end());
for_each(v.begin(), v.end(), myPrint);
cout << endl;
//利用sort进行降序
sort(v.begin(), v.end(), greater<int>());
for_each(v.begin(), v.end(), myPrint);
cout << endl;
}
int main() {
test01();
return 0;
}
random_shuffle
功能描述:洗牌 指定范围内的元素随机调整次序
函数原型: random_shuffle(iterator beg, iterator end);
//指定范围内的元素随机调整次序
//beg 开始迭代器
//end 结束迭代器
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<ctime>
//常用排序算法 random_shuffle
void myPrint(int val)
{
cout << val << " ";
}
void test01()
{
srand((unsigned int)time(NULL));
vector<int> v={1,2,3,4,5};
//利用洗牌打乱顺序
random_shuffle(v.begin(), v.end());
for_each(v.begin(), v.end(), myPrint);
cout << endl;
}
int main() {
test01();
return 0;
}
merge
功能描述:两个容器元素合并,并存储到另一个容器中
函数原型: merge(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);
//容器元素合并,并存储到另一个容器中
//注意:两个容器必须是有序的
//beg1 容器1开始迭代器
//end1 容器1结束迭代器
//beg2 容器2开始迭代器
//end2 容器2结束迭代器
//dest 目标容器开始迭代器
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
//常用排序算法 merge
void myPrint(int val)
{
cout << val << " ";
}
void test01()
{
vector<int> v1={1,3,5,9,11};
vector<int> v2={2,4,8,12};
//目标容器
vector<int> vTarget;
//提前给目标容器分配空间
vTarget.resize(v1.size() + v2.size());
merge(v1.begin(), v1.end(), v2.begin(), v2.end(), vTarget.begin());
for_each(vTarget.begin(), vTarget.end(), myPrint);
cout << endl;
}
int main() {
test01();
return 0;
}
reverse
功能描述:将容器内元素进行反转
函数原型: reverse(iterator beg, iterator end);
//反转指定范围的元素
//beg 开始迭代器
//end 结束迭代器
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
//常用排序算法 reverse
void myPrint(int val){
cout << val << " ";
}
void test01(){
vector<int> v={1,3,5,6,9};
cout << "before reverse" << endl;
for_each(v.begin(), v.end(), myPrint);
cout << endl;
cout << "after reverse" << endl;
reverse(v.begin(), v.end());
for_each(v.begin(), v.end(), myPrint);
cout << endl;
}
int main() {
test01();
return 0;
}
常用拷贝和替换算法
copy //容器内指定范围的元素拷贝到另一个容器中
replace //将容器内指定范围的旧元素修改为新元素
replace_if //容器内指定范围满足条件的元素替换为新元素
swap //交换两个容器的元素
copy
功能描述:容器内指定范围的元素拷贝到另一个容器中
函数原型: copy(iterator beg, iterator end, iterator dest);
//将开始迭代器到结束迭代器之间的元素拷贝到目标容器中
//beg 开始迭代器
//end 结束迭代器
//dest 目标开始迭代器
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
//常用拷贝和替换算法 copy
void myPrint(int val){
cout << val << " ";
}
void test01(){
vector<int> v1 = { 11,22,33,55,66,99 };
vector<int> v2;
v2.resize(v1.size());
copy(v1.begin(), v1.end(), v2.begin());
for_each(v2.begin(), v2.end(), myPrint);
cout << endl;
}
int main() {
test01();
return 0;
}
replace
功能描述:将容器内指定范围的旧元素替换为新元素
函数原型: replace(iterator beg, iterator end, oldvalue, newvalue);
//将区间内旧元素 替换成新元素
//beg 开始迭代器
//end 结束迭代器
//oldvalue 旧元素
//newvalue 新元素
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
//常用拷贝和替换算法 replace
class MyPrint
{
public:
void operator()(int val){
cout << val << " ";
}
};
void test01(){
vector<int> v={10,20,10,30,10,40};
cout << "before replace " << endl;
for_each(v.begin(), v.end(), MyPrint());
cout << endl;
//将10 替换成1000
replace(v.begin(), v.end(), 10, 1000);
cout << "after replace " << endl;
for_each(v.begin(), v.end(), MyPrint());
cout << endl;
}
int main() {
test01();
return 0;
}
replace_if
功能描述:将区间内满足条件的元素,替换成指定元素
函数原型: replace_if(iterator beg, iterator end, _pred, newvalue);
//按条件替换元素,满足条件的替换成指定元素
//beg 开始迭代器
//end 结束迭代器
//_pred 谓词
//newvalue 替换的新元素
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
//常用拷贝和替换算法 replace_if
class MyPrint{
public:
void operator()(int val) {
cout << val << " ";
}
};
class Greater20{
public:
bool operator()(int val) {
return val == 20;
}
};
void test01() {
vector<int> v={1,10,20,30,20,50,10};
cout << "before replace " << endl;
for_each(v.begin(), v.end(), MyPrint());
cout << endl;
//大于20 替换成1000
replace_if(v.begin(), v.end(), Greater20(), 1000);
cout << "after replace " << endl;
for_each(v.begin(), v.end(), MyPrint());
cout << endl;
}
int main() {
test01();
return 0;
}
swap
功能描述:互换两个容器的元素
函数原型: swap(container c1, container c2);
//互换两个容器的元素
//c1 容器1
//c2 容器2
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
//常用拷贝和替换算法 swap
void myPrint(int val){
cout << val << " ";
}
void test01(){
vector<int> v1={1,2,3};
vector<int> v2={11,22,33};
cout << "before swap " << endl;
for_each(v1.begin(), v1.end(), myPrint);
cout << endl;
for_each(v2.begin(), v2.end(), myPrint);
cout << endl;
cout << "after swap " << endl;
swap(v1, v2);
for_each(v1.begin(), v1.end(), myPrint);
cout << endl;
for_each(v2.begin(), v2.end(), myPrint);
cout << endl;
}
int main() {
test01();
return 0;
}
常用算术生成算法
accumulate //计算容器元素累计总和
fill //向容器中添加元素
算术生成算法属于小型算法,使用时包含的头文件为 #include<numeric>
accumulate
功能描述:计算区间内 容器元素累计总和
函数原型: accumulate(iterator beg, iterator end, value);
//计算容器元素累计总和
//beg 开始迭代器
//end 结束迭代器
//value 起始值
#include<iostream>
using namespace std;
#include<vector>
#include<numeric>
//常用算术生成算法
void test01(){
vector<int> v;
for (int i = 0; i <= 100; i++)
{
v.push_back(i);
}
int total = accumulate(v.begin(), v.end(), 0); //参数3 起始累加值
cout << " total = " << total << endl;
}
int main() {
test01();
return 0;
}
fill
功能描述:向容器中填充指定的元素
函数原型: fill(iterator beg, iterator end, value);
//向容器中填充元素
//beg 开始迭代器
//end 结束迭代器
//value 填充值
#include<iostream>
using namespace std;
#include<vector>
#include<numeric>
#include<algorithm>
//常用算术生成算法 fill
void myPrint(int val){
cout << val << " ";
}
void test01(){
vector<int> v;
v.resize(10);
//后期重新填充
fill(v.begin(), v.end(), 100);
for_each(v.begin(), v.end(), myPrint);
cout << endl;
}
int main() {
test01();
return 0;
}
常用集合算法
看6.5内容就好
7仿函数(functors)
7.1仿函数概观
仿函数是早期的命名,c++标准规格定案后所采用的的名称是函数对象。
就实现观点而言,仿函数其实上就是一个“行为类似函数”的对象,其类别定义中必须自定义function call运算子。拥有这样的运算子后,我们就可以在仿函数的对象后面加上一对小括号,以此调用仿函数所定义的operator()。
#include<functional>
#include<iostream>
using namespace std;
int main()
{
greater<int>ig;
cout << boolalpha << ig(4, 6); //false
cout << greater<int>()(6, 4); //true
}
其中第一种用法,greater<int>ig的意思是产生一个名为ig的对象,ig(4,6)则是调用operator(),并给予两个参数4和6.第二种用法中的greater<int>()意思是产生一个临时对象,之后的(4,6)才是指定两个参数4,6。
上面第二种用法对仿函数而言是主流用法。
STL仿函数的分类,若以操作数的个数划分,可分为一元和二元仿函数,若以功能划分,可分为算术运算、关系运算、逻辑运算三大类。
7.2可配接的关键
STL仿函数应该有能力被函数配接器修饰,彼此像积木一样地串接,为了拥有配接能力,每一个仿函数必须定义自己的相应型别。
仿函数的相应型别主要用来表现函数型别和传回值型别。为了方便起见头文件里面定义了两个classes,分别代表一元仿函数和二元仿函数。。任何仿函数,只要依个人需求选择继承其中一个class,便自动拥有了相应型别
7.2.1unary_funnction
unary_function用来呈现一元参数的参数型别和返回值型别
//STL规定,每一个Adaptable Unary Function都应该继承此类别
template<class Arg,class Result>
struct unary_function
{
typedef Arg argument_type;
typedef Result result_type;
};
一旦某个仿函数继承了unary_function,其用户便可以这样取得该仿函数的参数型别,并以相同手法取得其回返值型别
template<class T>
struct negate:public unary_function<T,T>
{
T operator()(const T& x)const (return -xor;)
};
7.2.2binary_function
binary_function用来呈现二元函数的第一参数型别,第二参数型别,以及回返值型别。
//STL规定,每一个Adaptable Binary Function都应该继承此类别
template<class Arg1,class Arg2,class Result>
struct binary_function
{
typedef Arg1 first_argument_type;
typedef Arg2 second_argument_type;
typedef Result result_type;
};
7.3算数类仿函数
STL内建的“算数类仿函数”,支持加法,减法,除法,模数和否定运算,除了否定运算为一元运算,其他都是二元运算。
加法:plus<T>
减法:minus<T>
乘法:multiplies<T>
除法:divides<T>
模取:modulus<T>
否定:negate<T>
template <class T>
struct plus:public binary_function<T,T,T>
{
constexpr T operator()(const T& lhs, const T& rhs) const{return lhs + rhs;}
};
template <class T>
struct minus :public binary_function<T, T, T>
{
constexpr T operator()(const T& lhs, const T& rhs) const { return lhs - rhs; }
};
template <class T>
struct multiplies :public binary_function<T, T, T>
{
constexpr T operator()(const T& lhs, const T& rhs) const { return lhs * rhs; }
};
template <class T>
struct divides :public binary_function<T, T, T>
{
constexpr T operator()(const T& lhs, const T& rhs) const { return lhs / rhs; }
};
template <class T>
struct modulus :public binary_function<T, T, T>
{
constexpr T operator()(const T& lhs, const T& rhs) const { return lhs % rhs; }
};
template <class T>
struct plus :public unary_function<T, T>
{
constexpr T operator()(const T& x) const { return -x; }
};
这些仿函数所产生的对象,用法和一般函数完全相同。当然也可以产生一个无名的临时对象来履行函数功能。
#include<functional>
#include<iostream>
using namespace std;
int main()
{
//以下产生一些仿函数实体
plus<int>plusobj;
minus<int>minusobj;
multiplies<int>multipliesobj;
divides<int>dividesobj;
modulus<int>modulusobj;
negate<int>negateobj;
//以下运用上述对象,履行函数功能
cout << plusobj(3, 5) << endl; //8
cout << minusobj(3, 5) << endl; //-2
cout << multipliesobj(3, 5) << endl;//15
cout << dividesobj(3, 5) << endl; //0
cout << modulusobj(3, 5) << endl; //3
cout << negateobj(3) << endl; //-3
}
7.4关系运算类仿函数
STL内建的“关系运算类仿函数”,支持等于、不等于、大于、大于等于、小于、小于等于,都是二元运算。
等于:equal_to<T>
不等于:not_equal_to<T>
大于:greater<T>
大于或等于:greater_equal<T>
小于:less<T>
小于或等于:less_equal<T>
7.5逻辑运算类仿函数
STL内建的“逻辑运算类仿函数”,支持逻辑运算中的And、Or、Not三种运算。其中And和Or为二元运算,Not为一元运算
逻辑运算 And:logical_and<T>
逻辑运算 Or:logical_or<T>
逻辑运算 Not:logical_not<T>