如果使用一种线性选择算法,则可以达到最坏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