快速选择(quick_select) 算法分析

来源:互联网 发布:破解linux系统root密码 编辑:程序博客网 时间:2024/06/06 12:51
快速选择算法,就是从给定的一个集合S={a1,a2,...an}中选出第K个大小的数,或者给出其所在的下标之类的。

如果使用排序,比如merge_sort,然后返回第K个元素的下标,复杂度是O(NlogN)

如果使用heap_sort,或者优先队列,则复杂度是O(NlogK)

如果使用quick _sort的一个变种,叫 quick select,则平均复杂度为O(N),最坏复杂度为O(N^2)

如果使用一种线性选择算法,则可以达到最坏O(N)的复杂度,不过实际应用中,该算法通常比quick select慢1到2倍,所以并不常用(参考Blum, Floyd, Pratt, Rivest, and Tarjan 1973  Time bounds for selection)

算法思想:

(1)利用快速排序的分治思想,求得待搜索数组按照的主元S[q](pivot)(主元的选定有好几种方法,这里不详细讨论,可参考快速排序),以主元为界分成左右两个区间

(2)通过比较主元的位置,判断第K个大小的数在主元左区间?在主元又区间?还是就是主元?(还要注意边界条件的判断,有可能在边界)

(3)进入子区间递归调用


这里实现了stl风格的quick select,仅仅作为一个mark

#include <algorithm>#include <cassert>namespace algorithm{template<typename _Tp>const _Tp& choose_pivot(const _Tp& x, const _Tp& y, const _Tp& z){        if( (x < y && y < z)||(z < y && y < x) )                return y;             else if( (z < x && x < y)||(y < x && x < z) )                return x;        else                return z;}template<typename _Tp,typename _Compare>const _Tp& choose_pivot(const _Tp& x, const _Tp& y,const _Tp& z, _Compare comp){        if( (comp(x,y) && comp(y,z))||(comp(z,y)&&comp(y,x)) )                return y;        else if( (comp(z,x) && comp(x,y))||(comp(y,x)&&comp(x,z)))                return x;        return z;}template<typename _RandomAccessIterator,typename _Tp>_RandomAccessIterator quick_partition(_RandomAccessIterator first,                _RandomAccessIterator last,_Tp pivot){        while( true ){                while( *first < pivot )                               ++first;                --last;                while( pivot < *last )                        --last;                if( first >= last )                        return first;                std::swap(*first,*last);                ++first;        }}template<typename _RandomAccessIterator,typename _Tp, typename _Compare>_RandomAccessIterator quick_partition(_RandomAccessIterator first,        _RandomAccessIterator last, _Tp pivot, _Compare comp){        while( true ){                while( comp(*first,pivot) == true )                           ++first;                --last;                while( comp(pivot,*last) == true )                        --last;                if( first >= last )                        return first;                std::swap(*first,*last);                ++first;                }}template<typename _RandomAccessIterator>_RandomAccessIterator quick_select(_RandomAccessIterator first, _RandomAccessIterator last, size_t kth){        typedef typename std::iterator_traits<_RandomAccessIterator>::value_type _ValueType;        typedef typename std::iterator_traits<_RandomAccessIterator>::difference_type _DistanceType;        if( first == last || last-first <=(_DistanceType)kth )//out of range                return last;        _ValueType pivot;        _RandomAccessIterator mid;        while( true )          {                if( kth == 0 )                                return std::min_element(first,last);                else if( first+kth == last - 1 )                        return std::max_element(first,last);                else{                        mid = first+(last-first)/2;                        pivot = choose_pivot(*first,*mid,*(last-1));                        mid = quick_partition(first,last,pivot);                        if( mid-first > (_DistanceType)kth )                                last = mid;                             else{                                kth -= mid-first;                                               first = mid;                        }                }                assert( last-first > (_DistanceType)kth);        }}template<typename _RandomAccessIterator,typename _Compare>_RandomAccessIterator quick_select(_RandomAccessIterator first, _RandomAccessIterator last, size_t kth,_Compare comp){        typedef typename std::iterator_traits<_RandomAccessIterator>::value_type _ValueType;        typedef typename std::iterator_traits<_RandomAccessIterator>::difference_type _DistanceType;        if( first == last || last-first <=(_DistanceType)kth )//out of range                return last;        _ValueType pivot;        _RandomAccessIterator mid;        while( true )          {                if( kth == 0 )                                return std::min_element(first,last,comp);                else if( first+kth == last - 1 )                        return std::max_element(first,last,comp);                else{                        mid = first+(last-first)/2;                        pivot = choose_pivot(*first,*mid,*(last-1),comp);                        mid = quick_partition(first,last,pivot,comp);                        if( mid-first > (_DistanceType)kth )                                last = mid;                             else{                                kth -= mid-first;                                               first = mid;                        }                }                assert( last-first > (_DistanceType)kth);        }}} //namespace


0 0
原创粉丝点击