zoukankan      html  css  js  c++  java
  • STL源码剖析(三)

    算法

    从语言的角度看:

    • 容器 Container 是一个class template
    • 算法 Algorithm 是一个function template
    • 迭代器 Iterator 是一个class template
    • 函数式 Functor 是一个class template
    • 适配器 Adapter 是一个class template
    • 分配器 Allocator 是一个class template

    Algorithm 和 Container 之间没有直接的联系,Algorithm 无法得知 Container 都有什么信息,所以需要通过 Iterator 来获取内部的信息,所以 Iterator 就必须要与 Algorithm 之间有这一定的交接规则,这样 Iterator 才能适配 Algorithm 的操作。

    Algorithm的大概形式如下:

    template<typename Iterator>
    Algorithm(Iterator itr1, Iterator itr2)
    {
        ...
    }
    
    template<typename Iterator, typename Cmp>
    Algorithm(Iterator itr1, Ilerator itr2, Cmp comp)
    {
        ...
    }
    

    迭代器的分类

    各种容器的 iterators 有5种 iterator_category

    struct input_iterator_tag {};
    struct output_iterator_tag {};
    struct forward_iterator_tag : public input_iterator_tag{};
    struct bidirectional_iterator_tag : public forward_iterator_tag{}
    struct random_access_iterator_tag : public bidirectional_iterator_tag{}
    

    Input 迭代器只能向前移动,一次一步,客户只有可读取(不能涂写)他们所指的东西,而且只能读取一次。它们模仿指向输入文件的阅读指针;C++ 程序库中的istream_iterator是这一分类的代表。

    Ouput 迭代器情况类似,但一切只为输入:它们只能向前移动,一次一步,客户只可涂写它们所指的东西,而且只能涂写一次。它们模仿指向输出文件的涂写指针;ostream_iterator是这一分类的代表。

    Forward 迭代器,这种迭代器可以做前两种迭代器的每一件事,而且可以读或写其所指物一次以上。这使得他们可施行于多次性操作算法。STL并未提供单向linked list,但某些程序库有(slist),而这种容器的迭代器就属于 forward 迭代器。

    Bidirectional 迭代器,除了可以向前移动,还可以向后移动。STL的list迭代器就属于这一分类,set, multiset,map 和 multimap 的迭代器也都是这一分类。

    random_access 迭代器可以在常量时间内向前或者向后跳跃任意距离。这样的算术很类似指针算术。

    迭代分类对算法的影响

    首先是一个distance迭代器之间距离的算法,distance算法通过对迭代器分类的判断分别调用不同的实现函数。其中,因为迭代器的分类有继承关系,再根据函数匹配的原则,不同分类的迭代器会自动选择适合的实现方法。

    template <class InputIterator>
    inline iterator_traits<InputIterator>::difference_type
    distance(InputIterator first, InputIterator last){
        typedef typename iterator_traits<InputIterator>::iterator_category category;
        return __distance(first, last, category());
    }
    
    template<class InputIterator>
    inline iterator_traits<InputIterator>::difference_type __distance(InputIterator first, InputIterator last, input_iterator_tag){
        iterator_traits<InputIterator>::difference_type n =0;
        while(first != last){
            ++first;
            ++n;
        }
        return n;
    }
    
    template <class RandomAccessIterator>
    inline iterator_traits<RandomAccessIterator>::difference_type __distance(RandomAccessIterator first, RandomAccessIterator last, random_access_iterator_tag){
        return last - first;
    }
    
    

    与distance类似的算法举例:

    template<class InputIterator, class Distance>
    inline void advance(InputIterator& i, Distance n){
        __advance(i, n, iterator_category(i));
    }
    
    //此方法与iterator_traits<InputIterator>::iterator_category一致
    template <class Iterator>
    inline typename iterator_traits<Iterator>::iterator_category iterator_category(const Iterator&){
        typedef typename iterator_traits<Iterator>::iterator_category category;
        return category();
    }
    
    template<class InputIterator, class Distance>
    inline void __advance(InputIterator& i, Distance n, input_iterator_tag){
        while(n--) ++i;
    }
    
    template<class BidirectionalIterator, class Distance>
    inline void __advance(BidirectionalIterator& i, Distance n, bidirectional_iterator_tag){
        if(n >= 0)
            while(n--) ++i;
        else
            while(n++) --i;
    }
    
    template<class RandomAccessIterator, class Distance>
    inline void __advance(RandomAccessIterator& i, Distance n, random_accrss_iterator_tag){
        i += n;
    }
    

    算法无法强制要求传入指定类型的迭代器,因为算法是一种模板,所以理论上可以传入所有类型的参数。为了尽可能保证算法正常工作,算法会“暗示”使用者出入怎样类型的迭代器。具体的实现是通过在 template<class RandomAccessIterator, class Distance> 这其中的 RandomAccessIterator 就是对传入类型的“暗示”。

    部分算法源码剖析

    accumulate

    template <class InputIterator, 
              class T>
    T accumulate(InputIterator first, 
                 InputIterator last, 
                 T init)
    {
        for(;first != last; ++first)
            init = init + *first;//将元素累加至初值init身上
        return init;
    }
    
    template <class InputIterator, 
              class T,
              class BinaryOperation>
    T accumulate(InputIterator first, 
                 InputIterator last, 
                 T init,
                 BinaryOperation binary_op)
    {
        for(;first != last; ++first)
            init = binary_op(init, *first);
        return init;
    }
    

    通常算法会有两个版本,一个版本适用默认的规则,另一个版本可以让用户传入一个自定义的“规则”。

    accumulate 使用的例子:

    #include <iostream>     //std::out
    #include <functional>   //std::minus
    #include <numeric>      //std::accumulate
    int myfunc(int x, int y) { return x + 2 * y;}
    //function object
    struct myclass{
        int operator()(int x, int y) { return x + 3 * y;}
    } myobj;
    
    int main()
    {
        int init = 100;
        int nums[] = {10, 20, 30};
    
        cout << "using default accumulate:";
        cout << accumulate(nuyms, nums+3, init); //160
        cout << "
    ";
    
        cout << "using functional's minus:";
        cout << accumulate(nuyms, nums+3, init, minus<int>()); //40
        cout << "
    ";
    
        cout << "using custom function:";
        cout << accumulate(nuyms, nums+3, init, mufunc); //220
        cout << "
    ";
    
        cout << "using custom object:";
        cout << accumulate(nuyms, nums+3, init, myobj); //280
        cout << "
    ";
    }
    

    for_each

    针对容器中的每一个元素都进行一次操作

    template <class InputIterator,
              class Function>
    Function for_each(InputIterator first,
                      InputIterator last,
                      Function f)
    {
        for(; first != last; ++first)
            f(*first);
        return f;
    }
    

    replace, replace_if, replace_copy

    replace:范围内所有等于old_value的元素都以new_value替换

    template <class ForwardIterator,
              class T>
    void replace(ForwardIterator first,
                 ForwardIterator last,
                 const T& old_value,
                 const T& new_value){
        for(;first != last; ++first)
            if(*first == old_value)
                *first = new_value;
    }
    

    replace_if:范围内所有满足pred()的元素都以new_value替换

    template <class ForwardIterator,
              class Predicate,
              class T>
    void replace_if(ForwardIterator first,
                 ForwardIterator last,
                 Predicate pred,
                 const T& new_value){
        for(;first != last; ++first)
            if(pred(*first))
                *first = new_value;
    }
    

    replace_copy:范围内所有等于old_value的元素都以new_value放入新的空间内

    template <class ForwardIterator,
              class T>
    void replace(ForwardIterator first,
                 ForwardIterator last,
                 OutputIterator result,
                 const T& old_value,
                 const T& new_value){
        for(;first != last; ++first, ++result)
            *result = *first == old_value ? new_value : *first;
        return result;
    }
    

    count, count_if

    count:统计等于value的元素个数

    template <class InputIterator, class T>
    typename iterator_traits<InputIterator>::difference_type 
    count(InputIterator first, 
          InputIterator last, 
          const T& value){
        typename iterator_traits<InputIterator>::difference_type n = 0;
        for(; first != last; ++first)
            if(*first == value)
                ++n;
        return n;
    }
    

    count_if:统计满足pred()value的元素个数

    template <class InputIterator, class Predicate>
    typename iterator_traits<InputIterator>::difference_type 
    count_if(InputIterator first, 
             InputIterator last, 
             Predicate pred){
        typename iterator_traits<InputIterator>::difference_type n = 0;
        for(; first != last; ++first)
            if(pred(*first))
                ++n;
        return n;
    }
    

    以上显示的标准库中的算法,有些容器使用标准库的算法效果不是很好或者效率不够高,那么这些容器会在成员函数中加入同名的函数。不带成员函数count()的容器有:array, vector, list, forward_list, deque;带成员函数count()的容器有:set/multiset, map/multimap, unordered_set/unordered_multiset, unordered_map/unordered_multimap。通过分析发现自带成员函数的这些容器都是关联性容器,可以依据key快速查找到value,所以实现自己特有的函数速度会更快。

    find, find_if

    find:循环遍历查找

    template <class InputIterator, class T>
    InputIterator find(InputIterator first,
                       InputIterator last,
                       const T& value)
    {
        while(first != last && *first != value)
            ++first;
        return first;
    }
    

    find_if:根据条件循环遍历查找

    template <class InputIterator, class T>
    InputIterator find(InputIterator first,
                       InputIterator last,
                       Predicate pred)
    {
        while(first != last && !pred(*first))
            ++first;
        return first;
    }
    

    count()一样,关联性容器有自己特有的find()成员函数。

    二分查找,前提是已经是有序序列!

    template <class ForwardIterator,
              class T>
    bool binary_search(ForwardIterator first,
                       ForwardIterator last,
                       const T& val)
    {
        first = std::lower_bound(first, last, val);
        return (first != last && !(val < *first>));
    }
    

    lower_bound: 在不影响原因顺序的前提下,找到可以插入的第一个位置。例如序列{10, 10, 10, 20, 20, 20, 30, 30, 30},现在需要插入20,则lower_bound返回指向第一个20的位置。同理,upper_bound指向最后一个20的后面。

    template <class ForwardIterator,
              class T>
    ForwardIterator lower_bound(ForwardIterator first,
                                ForwardIterator last,
                                const T& val)
    {
        ForwardIterator it;
        iterator_traits<ForwardIterator>::difference_type count, step;
        count = distance(first, last);
        while(count>0)
        {
            it = first;
            step = count/2;
            advance(it, step);
            if(*it < val)//或者可以是 if(comp (*it, val))
            {
                first = ++it;
                count -= step + 1;
            }
            else count = step;
        }
        return first;
    }
    
  • 相关阅读:
    Java虚拟机工作原理具体解释
    关于java的JIT知识
    php重建二叉树(函数缺省参数相关的都写在后面,比如array_slice函数中的$length属性,故第一个参数是操作的数组)
    php实现从尾到头打印列表
    thinkphp5项目--企业单车网站(九)(加强复习啊)(花了那么多时间写的博客,不复习太浪费了)
    php 面试题一(看视频的学习量比网上瞎转悠要清晰和明了很多)(看视频做好笔记)(注重复习)
    php对象和数组的相互转换(还是可以去找没有没php的高阶课程看看看)(要不别人分析一下重点要点,要不自己来,不然 效果真的不好)
    js中JSON的解析(将json字符串转化为对象)和序列化(将对象转化为json字符串)(函数的功能一般都挺全的,需要的时候去查看完整函数)
    html中radio、checkbox选中状态研究(静下心来看,静下心来总结)
    thinkphp中view页面中的volist标签转化为原生php分析(多去看源代码,你会发现不仅简单,方便你理解,还节约时间)
  • 原文地址:https://www.cnblogs.com/joker-wz/p/10266448.html
Copyright © 2011-2022 走看看