STL学习笔记--算法

关于STL算法需要注意的是:
(1) 所有STL算法被设计用来处理一个或多个迭代器区间。第一个区间通常以起点和终点表示,至于其他区间,多数情况下只需提供起点即可,其终点可自动以第一区间的元素数推导出来,故调用者必须确保区间的有效性。
(2) STL算法采用覆盖模式(overwrite),而非安插模式(insert).所以调用者必须保证目标区间拥有足够的元素区间。
(3) 所有的算法处理的都是半开区间[begin,end)。

下面逐一介绍一些常用的算法:

for_each算法

for_each(InputIterator beg, InputIterator end, UnaryProc op)

  • 对区间[beg,end)每一个元素调用op(elem)

  • 返回op的一个副本

  • op的任何返回值都会被忽略

  • 复杂度:线性。调用op numberOfElement次。

      void print(int elem)
      {
      	cout << elem << " ";
      }
      	
      template<typename T>
      struct AddValue
      {
      private:
      	T _theValueToAdd;
      public:
      	AddValue(const T & v)
      		: _theValueToAdd(v)
      	{
      		
      	}
      
      	void operator() (T & elem)
      	{
      		elem += _theValueToAdd;
      	}
      };
      
      //求平均值
      struct  MeanValue
      {
      public:
      	MeanValue()
      		: _num_of_elements(0)
      		, _sum(0)
      	{
      
      	}
      	void operator() (int elem)
      	{
      		_num_of_elements++;
      		_sum += elem;
      	}
      
      	//重载double类型运算符,没什么道理,只是返回平均值 
      	operator double()
      	{
      		return (static_cast<double>(_sum) / static_cast<double>(_num_of_elements));
      	}
      private:
      	long _num_of_elements;
      	long _sum;
      };
      
      //for_each算法
      vector<int> v;
      std::generate_n(std::back_inserter(v),10,rand);
      std::copy(v.begin(),v.end(),std::ostream_iterator<int>(std::cout," "));
      cout << endl;
    
      //一般函数作为for_each的参数
      std::for_each(v.begin(),v.end(), print);
    
      //函数对象作为for_each的参数
      //给v所有元素都加上100
      std::for_each(v.begin(),v.end(),AddValue<int>(100));
      
      cout << endl;
      std::copy(v.begin(),v.end(),std::ostream_iterator<int>(std::cout," "));
    
      //for_each的返回值 for_each返回op(一再算法内部被变动过的)一个副本
      //返回应该是函数对象,但是被转换成了double,这个是隐式转换
      double mv = std::for_each(v.begin(),v.end(),MeanValue());
      cout << "
    MeanValue: " << mv << endl;
    

transform 算法

transform提供两种能力:
(1) 4个参数形式。把源区间的元素转换到目标区间,即复制与修改一气呵成。

OutputInterator
tansform (InputIterator sourceBeg, InputInterator sourceEnd,
		OutputIterator destBeg, UnaryFunc op)
  • 针对源区间[sourceBeg,sourcEnd)调用op(elem),并将结果写到destBeg起始的目标区间。
  • 调用者必须确保目标区间有足够的空间。
  • sourceBeg与destBeg可以相同,所以,和for_each一样,可以使用transform来变动某一序列内的元素。transform的速度稍许些慢,因为它是将操作返回值赋值给元素,而不是直接变动元素。

(2) 5个参数形式,将前两个源序列中的元素合并,并将结果写入目标区间。即将两序列的元素结合。

OutputIterator
transform(InputIterator1 source1Beg, InputIterator1 source1End,
		InputIterator2 source2Beg,
		OutputIterator destBeg,
		BinaryFunc op)
  • 针对第一个源区间[source1Beg,source1End)以及从source2Beg开始的第二个源区间的相应元素,调用op(source1Elem,source2Elem),并将结果写入以destBeg起始的目标区间。

transform与for_each的区别
for_each()接受一项操作,该操作返回被改动后的参数。因此该参数必须以by reference方式传递。
transform()运用某项操作,该操作返回被改动后的参数。

transform使用实例:

string str("acCDeFG");
//transform典型应用 ,string大小写转换
std::transform(str.begin(),str.end(),str.begin(),toupper);

template<typename T>
struct Add
{
	Add(const T & v)
		: _theValueToAdd(v)
	{
	}
	T operator() (const T & elem)
	{
		return (_theValueToAdd + elem);
	}
	T _theValueToAdd;
};
//使用transform实现上述for_each的AddValue功能
std::transform(v.begin(),v.end(),v.begin(),Add<int>(1000));

int arr[] = {10,1,2,4,4,7};
list<int> list1(arr,arr + sizeof(arr) / sizeof(arr[0]));
std::copy(list1.begin(),list1.end(),std::ostream_iterator<int>(std::cout," "));

//实现对list中每个数值求平方
std::transform(list1.begin(),list1.end(),list1.begin(),list1.begin(),std::multiplies<int>());

互换元素内容 swap_ranges

//将区间[beg1, end1)内的元素和从beg2开始的区间内的对应元素互换  
//返回第二个区间中最后一个被交换元素的下一位置  
ForwardIterator2  
swap_ranges (ForwardIterator1 beg1, ForwardIterator1 end1,   
         	ForwardIterator2 beg2) 

注:如果要将相同型别的两个容器的全部元素互换,应该使用swap()成员函数,swap性能优异,她只交换容器的内部数据,即交换某些内部指针,所有时间复杂度为常数。

非变动性算法

(1) 元素计数

//计算区间[beg, end)中元素值等于value的元素个数  
difference _type  
count (InputIterator beg, InputIterator end, const T& value)  
  
//计算区间[beg, end)中令op(elem)元素判断结果为true的元素个数  
difference _type  
count_if (InputIterator beg, InputIterator end, UnaryPredicate op)  


int arr[] = {1,2,1,3,4,8,9,8};
vector<int> v(arr, arr + sizeof(arr)/sizeof(arr[0]));

//元素计数 求vector中大于com_num的元素个数
int comp_num = 4;
int num = std::count_if(v.begin(),v.end(),std::bind(std::greater<int>(),std::placeholders::_1,comp_num));

(2) 最大值最小值

如果存在多个最大值或最小值,算法返回找到的第一个。

//返回区间[beg, end)中最小值的位置,注意返回的是位置,你要通过 operator* 来得到值 
// 以operator< 进行元素比较 
InputIterator  
min_element (InputIterator beg, InputIterator end)  
  
//Op用来比较两个元素,op(elem1, elem2)  
InputIterator  
min_element (InputIterator beg, InputIterator end, CompFunc op)  
  
//返回区间[beg, end)中最大值的位置  
InputIterator  
max_element (InputIterator beg, InputIterator end)  
  
InputIterator  
max_element (InputIterator beg, InputIterator end, CompFunc op)  

(3) 搜索元素

搜索第一个匹配元素

//返回区间[beg, end)中第一个元素值等于value的元素位置,如果没有找到,两种形式都返回end  
InputIterator  
find (InputIterator beg, InputIterator end, const T& value)  
  
InputIterator  
find_if (InputIterator beg, InputIterator end, UnaryPredicate op)  

//搜寻元素 find 已序区间使用 binary_search
//关联式容器使用成员函数find()
std::find(v.begin(),v.end(),4);

(4) 区间比较

//判断区间[beg, end)内的元素是否都和以cmpBeg开头的区间内的元素相等  
bool  
equal (InputIterator1 beg, InputIterator1 end,   
       InputIterator2 cmpBeg)  
  
//判断区间[beg, end)内的元素和以cmpBeg开头的区间内对应的元素,  
//是否都能够使op(elem, cmpElem)为true  
bool  
equal (InputIterator1 beg, InputIterator1 end,   
       InputIterator2 cmpBeg, BinaryPredicate op)  



//区间比较equal 典型应用 不区分大小写比较字符串 string
struct NoCaseCompareStr
{
	bool operator() (char c1, char c2) const
	{
		return (toupper(c1) == toupper(c2));
	}
};

string src("abCD");
string dst("abcd");
bool isEqual = (src.size() == dst.size()
				&& std::equal(src.begin(),src.end(),dst.begin(),NoCaseCompareStr()));

移除性算法

注:移除性算法是在一个区间内移除某些元素。这些算法并不能改变元素的数量,只是以逻辑上的思考,将原本置于后面的“不移除的元素”向前移动,覆盖那些被删除的元素而已,返回新区间的逻辑终点。

//移除区间[beg, end)中每一个与value相等的元素  
ForwardIterator  
remove (ForwardIterator beg, ForwardIterator end,   
        const T& value)  
  
//移除区间[beg, end)中每一个令op(elem)为true的元素  
ForwardIterator  
remove_if (ForwardIterator beg, ForwardIterator end,   
           UnaryPredicate op)  



int arr[] = {1,2,1,3,4,8,9,8};
vector<int> v(arr, arr + sizeof(arr)/sizeof(arr[0]));
//删除所有大于4的元素
std::function<bool(int)> func = std::bind(std::greater<int>(),std::placeholders::_1,4);//绑定函数对象
//移除性算法只是返回一个新的终点,想把元素真正删除,还需要调用erase成员函数
v.erase(std::remove_if(v.begin(),v.end(),func),v.end());

注:
(1) 无法在关联式容器上使用移除型算法
(2) list提供了等效的成员函数remove,效率更高。

排序算法

关联式容器自动排序,但对全体元素进行一次性排序,通常比始终维护它们保持已序状态来得高效一些。

(1) 对所有元素排序

//排序,缺省使用 operator< 排序 
void  
sort (RandomAccessIterator beg, RandomAccessIterator end)  
  
//排序,使用op(elem1, elem2)准则  
void  
sort (RandomAccessIterator beg, RandomAccessIterator end,   
      BinaryPredicate op)  
  
//保证相等元素的原本相对次序在排序后保持不变  
//换句话出,相等的元素它会按照固有的顺序,比如字典顺序排序  
void  
stable_sort (RandomAccessIterator beg, RandomAccessIterator end)  
  
void  
stable_sort (RandomAccessIterator beg, RandomAccessIterator end,   
             BinaryPredicate op)  

//降序排列
std::sort(v.begin(),v.end(),std::greater<int>());

注:
不可以对list调用这些算法,list不支持随机存取迭代器,可以使用list自己提供的成员函数sort().

(2) 局部排序

//以 < 对区间[beg, end)内的元素进行排序,使区间[beg, sortEnd)内的元素有序  
void  
partial_sort (RandomAccessIterator beg,   
              RandomAccessIterator sortEnd,   
              RandomAccessIterator end)  
  
//以op(elem1, elem2)对区间内元素进行排序  
void  
partial_sort (RandomAccessIterator beg,   
              RandomAccessIterator sortEnd,   
              RandomAccessIterator end,   
              BinaryPredicate op)  

和sort不同,partial_sort并不对全部元素排序:一旦第一个元素至sortEnd之前的所有元素都排拖次序,就立即停止。

对sortEnd-beg个元素进行排序,也就是说,如果sortEnd-beg等于42,则该函数将有序次序中的最小值元素放在序列中的前42个位置。partial_sort完成之后,从beg到sortEnd(但不包括sortEnd)范围内的元素时有序的,已排序范围内没有元素大于sortEnd之后的元素。未排序元素之间的次序是未指定的。
(缺省升序排列)
例:

int arr[] = {1,2,1,3,4,8,9,8};
vector<int> v(arr, arr + sizeof(arr)/sizeof(arr[0]));

vector<int> v1(v);
vector<int> v2(v);

PrintElements(v1,"V1 and V2");
//局部排序 取前3个排序元素
std::partial_sort(v1.begin(),v1.begin() + 3,v1.end(),std::greater<int>());
PrintElements(v1,"PartialSort");
//全排序	
std::sort(v2.begin(),v2.end(),std::greater<int>());
PrintElements(v2,"AllSort");

partial_sort

备注:
sort内部采用quicksort算法
partial_sort内部采用heapsort算法
stable_sort内部采用mergesort算法

(3) 根据第n个元素排序

如果只需要排序后的第n各元素,或只需令最先或最后的n个元素(无序)就位,这个算法就很有用。

//对区间[beg, end)内的元素进行排序,使第n个位置上的元素就位  
//也就是n之前的元素都小于等于它,n之后的元素都大于等于它  
//但是n前后的元素不要求有序  
void  
nth_element (RandomAccessIterator beg,   
             RandomAccessIterator nth,   
             RandomAccessIterator end)  
  
void  
nth_element (RandomAccessIterator beg,   
             RandomAccessIterator nth,   
             RandomAccessIterator end,   
             BinaryPredicate op)  

类似的算法parttition,根据某个准则,将序列中的元素分为两部分。

//将区间[beg, end)中使op(elem)为true的元素向前移动  
BidirectionalIterator  
partition (BidirectionalIterator beg, BidirectionalIterator end,  
           UnaryPredicate op)  
  
//会保持它们之前的相对次序  
BidirectionalIterator  
stable_partition (BidirectionalIterator beg, BidirectionalIterator end,   
                  UnaryPredicate op)  

nth_elementpartition的区别:
nth_element调用之后,并不精确知道第一子集和第二子集之间有什么不同,两部分都可能包含和第n各元素相等的元素。
partiton调用之后,并不知道第一和第二子集内各有多少个元素。返回的pos指出第二个子集的起点,第二子集的所有元素都不满足上述传入的准则。

已序区间算法

针对已序区间的算法,前提是源区间必须在某个排序准则下已序,有一定的性能优势。
即使迭代器并非随机存取型,也可以使用这些算法,此时算法的复杂度会降为线性。

//判断已序区间[beg, end)是否包含和value等值的元素  
bool  
binary_search (ForwardIterator beg, ForwardIterator end,   
               const T& value)  
  
bool  
binary_search (ForwardIterator beg, ForwardIterator end,   
               const T& value,   
               BinaryPredicate op)  

//返回第一个"大于等于value"的元素位置,如果不存在返回end  
ForwardIterator  
lower_bound (ForwardIterator beg, ForwardIterator end,   
             const T& value)  
  
ForwardIterator  
lower_bound (ForwardIterator beg, ForwardIterator end,   
             const T& value,   
             BinaryPredicate op)  
  
//返回第一个"大于value"的元素位置  
ForwardIterator  
upper_bound (ForwardIterator beg, ForwardIterator end,   
             const T& value)  
  
ForwardIterator  
upper_bound (ForwardIterator beg, ForwardIterator end,   
             const T& value,   
             BinaryPredicate op)  

//返回"与value相等"的元素所形成的区间  
pair<ForwardIterator,ForwardIterator>  
equal_range (ForwardIterator beg, ForwardIterator end,   
             const T& value)  
  
pair<ForwardIterator,ForwardIterator>  
equal_range (ForwardIterator beg, ForwardIterator end,   
             const T& value,   
             BinaryPredicate op)  

//source1: 1 2 2 4 6 7 7 9  
//source2: 2 2 2 3 6 6 8 9  
//Sum:     1 2 2 2 2 2 3 4 6 6 6 7 7 8 9 9  
//将源区间[source1Beg, source1End)和[source2Beg, source2End)内的元素合并  
//以destBeg为起点输出  
OutputIterator  
merge (InputIterator source1Beg, InputIterator source1End,   
       InputIterator source2Beg, InputIterator source2End,   
       Output Iterator destBeg)  
  
OutputIterator  
merge (InputIterator source1Beg, InputIterator source1End,   
       InputIterator source2Beg, InputIterator source2End,   
       OutputIterator destBeg,   
       BinaryPredicate op)
原文地址:https://www.cnblogs.com/cmranger/p/4728857.html