zoukankan      html  css  js  c++  java
  • 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)
  • 相关阅读:
    PHP中可执行代码小结
    HTTP请求头列举
    GET 和 POST详解
    利用kali自带的木马工具来远程控制服务器
    利用PHP木马,远程链接服务器
    利用sqlmap暴力破解数据库密码
    MIT线性代数课程 总结与理解-第一部分
    [转]MFC debug 调试信息的输出
    关于利用均匀分布随机变量产生任意分布变量的实现
    关于string转整数
  • 原文地址:https://www.cnblogs.com/cmranger/p/4728857.html
Copyright © 2011-2022 走看看