zoukankan      html  css  js  c++  java
  • STL源码剖析:算法

    • 算法,问题之解法也

    • 算法好坏的衡量标准:时间和空间,单位是对数、一次、二次、三次等

    • 算法中处理的数据,输入方式都是左闭又开,类型就迭代器, 如:[first, last)

    • STL中提供了很多算法,我们只研究感兴趣的几种

    copy函数

    • 拷贝[first, last)到[result, reslut+(last - first))

    • 总体考虑:对象能够直接在内存级别拷贝,还是需要单独拷贝

    • 设计技巧:重载和特化

    • 如下图所示:

    template<class InputIterator, class OutputIterator>
    inline OutputIterator copy(InputIterator first, InputIterator last, OutputIterator reslut)
    {
        return __copy_dispatch<InputIterator, OutputIterator>()(first, last, result);
    }
    
    // 特化
    inline char* copy(const char* first, const char* last, char* result)
    {
        memmove(result, first, last - first);
        return result + (last - first);
    }
    
    // 特化
    inline wchar_t* copy(const wchar_t* first, const wchar_t* last, wchar_t* result)
    {
        memmove(result, first, sizeof(wchar_t) * (last - first));
        return result + (last - first);
    }
    template<class InputIterator, class OutputIterator>
    struct __copy_dispatch
    {
        OutputIterator operator()(InputIterator first, InputIterator last, OutputIterator reslut)
        {
            return __copy(first, last, result, iterator_category(first));
        }
    }
    
    // 特化
    template <class T>
    struct __copy_dispatch<T*, T*>
    {
        T* operator()(T* first, T* last, T* result)
        {
            typedef typename __type_traits<T>::has_trival_assignment_operator t;
            return __copy_t(first, last, result, t());
        }
    }
    
    // 特化
    template <class T>
    struct __copy_dispatch<const T*, T*>
    {
        T* operator()(const T* first, const T* last, T* result)
        {
            typedef typename __type_traits<T>::has_trival_assignment_operator t;
            return __copy_t(first, last, result, t());
        }
    }
    template<class InputIterator, class OutputIterator>
    inline OutputIterator __copy(InputIterator first, InputIterator last, OutputIterator reslut, input_iterator_tag)
    {
           // 以判断迭代器是否相同为标准,速度慢
        for(; first != last; ++result, ++first)
        {
            *result = *first;
        }
        return result;
    }
    
    template<class InputIterator, class OutputIterator>
    inline OutputIterator __copy(InputIterator first, InputIterator last, OutputIterator reslut, random_access_iterator_tag)
    {
        // 完全是为了复用
        return __copy_d(first, last, result, distance_type(first));
    }
    
    template<class InputIterator, class OutputIterator, class Distance>
    inline OutputIterator __copy_d(InputIterator first, InputIterator last, OutputIterator reslut, Distance*)
    {
        // 以判断n值是否大于0为标准,速度快
        for(Distance n = last - first; n > 0; --n, ++result, ++first)
        {
            *result = *first;
        }
        return result;
    }
    templat <class T>
    inline T* __copy_t(const T* first, const T* last, T* result, __true_type)
    {
        // 直接复制内存
        memmove(result, first, sizeof(T) * (last - first));
        return result + (last - first);
    }
    
    templat <class T>
    inline T* __copy_t(const T* first, const T* last, T* result, __true_type)
    {
        // 每个数据单独复制
        return __copy_d(first, last, result, (ptrdiff_t*)0);
    }

    copy_back函数

    • copy_back和copy的设计方式基本相同,问题的区别是拷贝的方向不同,copy是从first开始到last拷贝,copy_back是从last开始到first拷贝

    • copy_back的拷贝过程:*(result - 1) = *(last - 1), *(result - 2) = *(last - 2)...

    find函数

    • 在[first, last)中找出第一个匹配的数据,返回指向该数据的Iterator

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

    sort函数

    • 插入排序

      • 优点:对于小型,基本有序的数据进行排序,效率最高

      • 缺点:对于大型数据,完全无序,效率非常低

    • 堆排序

      • 优点:对大型数据表现良好,所需的额外存储空间是和数据等同的大小

      • 缺点:对于小型数据不合适

      • 复杂度:平均O(NlogN),最坏O(N^2)

    • 快速排序

      • 优点:对于大型数据表现良好

      • 缺点:递归调用耗资源,不适用于小型数据

      • 复杂度:平均O(NlogN),最坏O(NlogN)

    • STL中的排序算法

      • 如果数据个数大于16,使用快速排序,如果快速排序递归的层次超过一定阈值,使用堆排序

      • 如果数据小于16,直接使用插入排序

      • 原因:

        • 小数据直接使用插入排序,效率高

        • 大型数据一开始就使用堆排序,复杂度是O(NlogN),一开始使用快速排序效率低于O(NlogN),如果一直使用快速排序,算法的复杂度会降低到最坏O(N^2),所以先快速排序再堆排序

    • 插入排序算法源码

    template <class RandomAccessIterator>
    void __insert_sort(RandomAccessIterator first, RandomAccessIterator last)
    {
        if(first == last)
        {
            return;
        }
        
        for(RandomAccessIterator i = first + 1; i != last,; i++)
        {
            __linear_insert(first, i, value_type(first));
        }
    }
    
    template <class RandomAccessIterator, class T>
    inline void __linear_insert(RandomAccessIterator first, RandomAccessIterator last, T*)
    {
        T value = *last;
        if(value < first)
        {
            // 需要插入的值比头部的值还小,直接整体拷贝
            copy_back(first, last, last+1);
            *first = value;
        }
        else
        {
            // 从后向前,依次比较拷贝
            __unguarded_linear_insert(last, value);
        }
    }
    
    template <class RandomAccessIterator, class T>
    void  __unguarded_linear_insert(RandomAccessIterator last, T value)
    {
        --next;
        while(value < *next)
        {
            *last = *next;
            last = next;
            --next;
        }
        *last = value;
    }
    • 快速排序算法源码
    // 取三值中点
    template <class T>
    inline const T& _median(const T& a, const T& b, const T& c)
    {
        if(a < b)
        {
            if(b < c)
            {
                return b;
            }
            else if(a < c)
            {
                return c;
            }
            else
            {
                return a;    
            }
        }
        else if(a < c)
        {
            return a;
        }
        else if(b < c)
        {
            return c;
        }
        else
        {
            return b;
        }
    }
    template <class RandomAccessIterator, class T>
    RandomAccessIterator __unguarded_partition(RandomAccessIterator first, RandomAccessIterator last, T pivot)
    {
        while(true)
        {
            while(*first < pivot)
            {
                ++first;
            }
            
            --last;
            while(pivot < *last)
            {
                --last;
            }
            
            if(!(first < last))
            {
                return first;
            }
            
            iter_swap(first, last);
            ++first;
        }
    }    
    • 堆排序算法源码

      • 见序列式容器中的堆章节

    • STL中的排序算法源码

    template <class RandomAccessIterator>
    inline void sort(RandomAccessIterator first, RandomAccessIterator last)
    {
        if(first != last)
        {
            // 快速排序和堆排序
            __introsort_loop(first, last, value_type(first), __lg(last - first) * 2);
            // 插入排序
            __final_insertion_sort(first, last)
        }
    }
    
    // 找出2^k < n的最大k值
    templat <class Size>
    inline Size __lg(Size n)
    {
        Size k;
        for(k = 0; n > 1; n >> 1)
        {
            ++k;
        }
        return k;
    }
    
    template <class RandomAccessIterator, class T, class Size>
    void __introsort_loop(RandomAccessIterator first, RandomAccessIterator last, T*, Size depth_limit)
    {
        while(last - first > 16)
        {
            if(depth_limit == 0)
            {
                partial_sort(first, last, last); // 堆排序
                return;
            }
            
            --depth_limit;
            // 取中值
            RandomAccessIterator cut = __unguarded_partition(first, last, 
                                                          T(_median(
                                                             *first,
                                                             *(first + (last - first) / 2),
                                                             *(last - 1)
                                                          )));
            // 右半段递归sort
            __introsort_loop(cut, last, value_type(first), depth_limit);
            
            // 左半段在while中递归sort
            last = cut;
        }
    }
    
    template <class RandomAccessIterator>
    void __final_insertion_sort(RandomAccessIterator first, RandomAccessIterator last)
    {
        if(last - first > 16)
        {
            // 以下写法感觉有点冗余。先排序前16个数据,然后将后需要数据依次插入排序
            __insertion_sort(first, first + 16);
            __unguarded_insertion_sort(first + 16, last);
        }
        else
        {
            // 小于16,直接插入排序
            __insert_sort(first, last);
        }
    }
    
    template <class RandomAccessIterator>
    inline void __unguarded_insertion_sort(RandomAccessIterator first, RandomAccessIterator last)
    {
        __unguarded_insertion_sort_aux(first, last, value_type(first));
    }
    
    template <class RandomAccessIterator, class T>
    void __unguarded_insertion_sort_aux(RandomAccessIterator first, RandomAccessIterator last, T*)
    {
        for(RandomAccessIterator i = first; i != last; ++i)
        {
            __unguarded_linear_insert(i, T(*i));
        }
    }
  • 相关阅读:
    保研练习题(5)
    保研练习题(4)
    保研练习题(3)
    保研练习题(2)
    保研练习题(1)
    基于邻接矩阵的拓扑排序--升级版
    基于邻接矩阵的拓扑排序
    vue 父组件调用子组件的函数
    vue 子组件调用父组件的函数
    JS 打乱数组顺序
  • 原文地址:https://www.cnblogs.com/chusiyong/p/11574252.html
Copyright © 2011-2022 走看看