C++标准库学习之 STL 迭代器算法简单学习

24 阅读3分钟

为了处理容器内的数据, STL 为我们提供了一系列的算法 包括 查找 排序 拷贝 重新排序 修改 数值运算等基本而普遍的算法.

算法并不是容器的成员函数,而是一种搭配迭代器使用的全局函数,这样做大幅减少程序的代码量,提高程序库的能力和弹性

先从 STL 简单的算法开始, 算法在 < algorithm > 包下,


#include <iostream>
#include <vector>
#include <algorithm>

/**
 * 在迭代器的使用过程中,尽量使用 const_iterator 迭代器,
 * 来保证数据的准确性,防止被他人修改
 */
void tsm_algorithm(){
    vector<int> tsm={4,2,5,6,1,3,5,3,2,7};

    // 求最小值
    auto min=min_element(tsm.cbegin(),tsm.cend());
    cout<<"min:"<< *min<<endl;

    //求最大值
    auto max=max_element(tsm.cbegin(),tsm.cend());
    cout << "max:"<< *max<<endl;

    // 排序 ,不能使用 const_iterator,只读无法更改数据
    sort(tsm.begin(),tsm.end());
    for(auto item:tsm){
        cout<<"排序后遍历:" <<item<<endl;
    }

    // 查找
    auto index= find(tsm.cbegin(),tsm.cend(),9);
    if(index==tsm.cend()){
        cout <<"查找失败"<<endl;
    }else{
        cout <<"查找成功后的结果:"<< *index <<endl;
    }

    //反转数据 不能使用 const_iterator,只读无法更改数据
    reverse(tsm.begin(),tsm.end());

    for(auto item:tsm){
        cout<<"反转后遍历:" <<item<<endl;
    }

}

结果:

D:\CWorkSpace\tsmTest\cmake-build-debug\tsmTest.exe
min:1
max:7
排序后遍历:1
排序后遍历:2
排序后遍历:2
排序后遍历:3
排序后遍历:3
排序后遍历:4
排序后遍历:5
排序后遍历:5
排序后遍历:6
排序后遍历:7
查找失败
反转后遍历:7
反转后遍历:6
反转后遍历:5
反转后遍历:5
反转后遍历:4
反转后遍历:3
反转后遍历:3
反转后遍历:2
反转后遍历:2
反转后遍历:1

Process finished with exit code 0

在迭代器的使用过程中,尽量使用 const_iterator 迭代器,来保证数据的准确性,防止被他人修改

这里先从 min_element 这迭代器来看看他的源码是如何实现的

template<typename _ForwardIterator>
  _GLIBCXX14_CONSTEXPR
  _ForwardIterator
  inline min_element(_ForwardIterator __first, _ForwardIterator __last)
  {
    // concept requirements
    __glibcxx_function_requires(_ForwardIteratorConcept<_ForwardIterator>)
    __glibcxx_function_requires(_LessThanComparableConcept<
   typename iterator_traits<_ForwardIterator>::value_type>)
    __glibcxx_requires_valid_range(__first, __last);
    __glibcxx_requires_irreflexive(__first, __last);

    return _GLIBCXX_STD_A::__min_element(__first, __last,
      __gnu_cxx::__ops::__iter_less_iter());
  }


 template<typename _ForwardIterator, typename _Compare>
   _GLIBCXX14_CONSTEXPR
   _ForwardIterator
   __min_element(_ForwardIterator __first, _ForwardIterator __last,
     _Compare __comp)
   {
     if (__first == __last)
return __first;
     _ForwardIterator __result = __first;
     while (++__first != __last)
if (__comp(__first, __result))
  __result = __first;
     return __result;
   }

在 min_element 方法中,拿到 first 和 end 迭代器,同时使用 first 和 end 外加 __gnu_cxx::__ops::__iter_less_iter() 这个方法一起调用了 __min_element 这个方法,

在 __min_element 方法中 先判断开始与结束是否一致,如果一致则返回first ,不一致则 使用while 循环调用 compare 方法,来获取最小值

我们再来介绍一下find 方法,因为find 可能存在两种情况,一种是找到了,一种是没找到,那么一个指针如何来判断是否找到呢,来看看他的源码

template<typename _InputIterator, typename _Tp>
  inline _InputIterator
  find(_InputIterator __first, _InputIterator __last,
const _Tp& __val)
  {
    // concept requirements
    __glibcxx_function_requires(_InputIteratorConcept<_InputIterator>)
    __glibcxx_function_requires(_EqualOpConcept<
typename iterator_traits<_InputIterator>::value_type, _Tp>)
    __glibcxx_requires_valid_range(__first, __last);
    return std::__find_if(__first, __last,
       __gnu_cxx::__ops::__iter_equals_val(__val));
  }
  

template<typename _Iterator, typename _Predicate>
  inline _Iterator
  __find_if(_Iterator __first, _Iterator __last, _Predicate __pred)
  {
    return __find_if(__first, __last, __pred,
       std::__iterator_category(__first));
  }

  
   inline _InputIterator
   __find_if(_InputIterator __first, _InputIterator __last,
      _Predicate __pred, input_iterator_tag)
   {
     while (__first != __last && !__pred(__first))
++__first;
     return __first;
   }

可以看到find 其实是与 min_element 的写法是非常类似的,整个过程都是在不断的从first 向 end 处遍历,如果遍历不到最后返回的指针应该与 end 重叠,我们只要判断返回的 iterator 是否与 end 重叠即可知道是否查找成功

也就是我在案例中使用的
auto index= find(tsm.cbegin(),tsm.cend(),9); if(index==tsm.cend()){ cout <<"查找失败"<<endl; }else{ cout <<"查找成功后的结果:"<< *index <<endl; }

这个里面还有一个非常小的细节,那就是在传入 first 和 end 时,不能同时传入 iterator 与 const_iterator ,下面这个就是不允许的

auto min=min_element(tsm.cbegin(),tsm.end());

在上面同时使用了 cbegin 和 end ,编译器会提示你使用方式不正确,原因是

template<typename _ForwardIterator>

所有的方法只接受一种 template ,cbegin 和 end 是两种类型,所以不可以