STL sort算法技巧和基本排序算法实现

来源:互联网 发布:淘宝怎么上掌柜热卖 编辑:程序博客网 时间:2024/05/02 03:05
1.基本排序算法之一 冒泡排序  时间复杂度  o(n^2)  实现简单
//stable sort  time (o(n^2))
template<class RandomAccessIterator >
void __bubble_sort_myself( RandomAccessIterator first ,  RandomAccessIterator last)
{

        if(first == last) return ;
        typedef typename iterator_traits<RandomAccessIterator>::difference_type distance;
        distance len = last - first ;
        distance i = 0 , j = 0 ;
        for ( ; i < len ; i++ )
        {
                for(j = 0 ; j < len -i-1 ; j++)
                {
                        if(*(first+j) > *(first+j+1))
                        iter_swap(first+j,first+j+1);
                }
        }
}
本算法小技巧,用len控制循环,比迭代器比较要稍快
2.基本排序算法之二 插入排序  时间复杂度  o(n^2)  实现简单
//插入排序   稳定  time (o(n^2))
template<class RandomAccessIterator >
void __insert_sort_myself( RandomAccessIterator first ,  RandomAccessIterator last)
{
        if(first == last) return ;
        typedef typename iterator_traits<RandomAccessIterator>::difference_type distance;
        typedef typename iterator_traits<RandomAccessIterator>::value_type value_type;
        distance len = last - first ;
        distance i  , j ;
        value_type  temp ;
        for ( i = 1 ; i < len ; i++)
        {
                temp = *(first + i) ;
                if(temp < *first )//如果是第一个之前的数  直接copy
                {
                copy_backward(first ,first + i  ,first + i + 1);
                *first = temp;
                }
                else //因为temp必然在范围之内,所以循环比较 ,减去边界比较
                {
                        __unguarded_linear_insert_sgi(first + i ,temp);
                }
        }
}
//插入排序的辅助函数 这里还可以优化   就是位置查找的方式可以选择二分查找方式
template<class RandomAccessIterator ,class T>
void __unguarded_linear_insert——sgi( RandomAccessIterator last ,  const T& temp)
{
        RandomAccessIterator i = last;
        --i;
        while(*i > temp)
        {
                *last = *i;
                last = i;
                --i;
        }
        *last = temp;
}
ps:主要STL技巧就在这个辅助函数里面,前面判断了temp必然在范围之内,所以只需要比较交换就可以,不用去判断边界条件,算是一种优化吧

3.基本排序算法之三 快速排序  时间复杂度  o(nlogn)  最坏情况n^2
template<class T >//取中值函数
inline const T& __midian_myself(const T& a,const T& b,const T& c)
{
        if( a > b)
                if ( b > c)
                return b;
                else
                return c > a ? a : c;

                else if ( a > c)
                return a;
                else
                return b > c ? c : b ;
}
template<class RandomAccessIterator , class T>//辅助函数,用于将元素分隔
RandomAccessIterator __unguarded_partition_myself( RandomAccessIterator first ,  RandomAccessIterator last , const T& t)
{
        while ( true )
        {
                while ( *first < t ) ++first;
                --last;
                while ( *last > t ) --last;
                if(!(first < last)) return first;
                iter_swap( first , last) ;
                ++first ;
        }
}
//快速排序   不稳定  time (o(n*logn))  最坏情况o(n^2)
template<class RandomAccessIterator >
void __quick_sort_myself( RandomAccessIterator first ,  RandomAccessIterator last)
{
        if(first == last) return;
        RandomAccessIterator i = first + ( last -first ) / 2 ;
        typedef typename iterator_traits<RandomAccessIterator>::value_type value_type;

        const value_type value = __midian_myself( *first ,*--last ,*i);//取3点中值
        ++last;
        RandomAccessIterator j = __unguarded_partition_myself(first ,last , value);
        //必要判别条件  
        if( j > first + 1 )
        __quick_sort_myself(first , j);
        if( last > j + 1 )
        __quick_sort_myself(j , last);
}
ps:本算法花了蛮久时间,一直在纠结的是存在相同元素的情况,对于辅助函数的思考,就是分隔了以后不一定是大于的都在左边,小于的都在右边,有可能有等于的在两边,但是最终在多次递归之后,相同元素还是会聚合在一起!还有就是边界问题老出问题。见红色部分,必须判断。取中点的法式是用于避免最坏情况的发生。

4.基本排序算法之三 sgi_stl快速排序  时间复杂度  o(nlogn)  最坏情况约等于nlogn

//SGI STL sort  大部分情况用三点均值快速排序,分割恶化出现用Heap_sort也就是partial_sort,在小的子序列就用insert_sort
template<class RandomAccessIterator >
void __quick_sort_sgi( RandomAccessIterator first ,  RandomAccessIterator last)
{
        if(first != last)
        {
        __introsort_loop_sgi(first , last ,value_type( first ), __lg((last-first) *2) );
        __final_insertion_sort_sgi(first , last );
        }
}
//取log的函数
template<class Size>
inline Size  __lg(Size n)
{
        Size k;
        for( k = 0 ; n >1 ; n>>=1) ++k;
        return k;
}
//一般情况快速排序,恶化用堆排序(o(nlogn))总体效率趋近nlogn
template<class RandomAccessIterator , class T, class Size>
void __introsort_loop_sgi(RandomAccessIterator first ,RandomAccessIterator  last , T* , Size depth_limit)
{
        while ( last - first > 16)
        {
                //分割恶化,就是说经过几次分割后仍有大部分元素在一起。
                if(depth_limit  == 0)
                {
                        //等同于先make_heap();然后sort_heap();
                        partial_sort(first , last ,last);
                        return;
                }
                --depth_limit;
                T value = __midian_myself(*first,    *(first + (last- first)/2),    *(last-1));
                RandomAccessIterator cur = __unguarded_partition_sgi(first , last , value);
                //递归调用  不断调整尾部位置就行了
                __introsort_loop_sgi(cur , last , value_type(first), depth_limit);
                last = cur;
        }

}
template<class RandomAccessIterator >
void __final_insertion_sort_sgi( RandomAccessIterator first ,  RandomAccessIterator last)
{
//对前面的序列排序好之后,后面的都在范围内,所以采用不判定边界,只比较大小的函数__unguarded_linear_insert
        if(last - first > 16)
        {
                __insert_sort_myself(first , first +16);
                __unguarded_insertion_sort_sgi(first + 16 , last);
        }
        else
                __insert_sort_myself(first , last);
}


//主要是为来不要判定边界条件作优化
template<class RandomAccessIterator >
inline void __unguarded_insertion_sort_sgi( 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_sgi(i , T(*i));//利用不判定边界的方式 

}

ps:本次试验的总结就是要注意函数名称递归函数名称不要写错,函数名最好区分于SGI函数名

5.基本排序算法之三归并排序  时间复杂度  o(nlogn)  最坏情况nlogn 采用迭代实现
//归并排序 不稳定 nlogn  
template<class RandomAccessIterator >
void __merge_sort_myself( RandomAccessIterator first ,  RandomAccessIterator last)
{
        if(first == last)return;
        typedef typename iterator_traits<RandomAccessIterator>::difference_type distance;
        distance  len = last -first ;
        for ( distance depth = 0 ; depth < __lg( len ) + 1 ; ++depth)
        {
                //从归并位置尾端开始,主要是防止出界运算!      
                distance pos =  pow(2 , depth + 1);
                for(pos ; pos < len; pos += pow(2,depth+1))
                __insert_sort_myself ( first + pos - pow(2,depth+1) , first + pos  );


               //对后面几个在排序
                __insert_sort_myself ( first + pos - pow(2,depth+1), last);
        }

}
PS:归并排序的__insert_sort 可以用merge整合,归并排序主要是注意越界问题 但是如果用merge就要占用N个空间,消耗掉蛮多拷贝复制的资源。


5.基本排序算法之三堆排序  时间复杂度  o(nlogn)  最坏情况nlogn 
其中算法复杂度主要有建立堆和调整堆的时间来决定。
算法通过不断的push来建立堆和不断的pop来输出序列
数据结构采用的是顺序存储,并且是Random迭代器

//建堆 遍历  注意传到push里面的尾节点位置不是last  而是last -1
template<class RandomAccessIterator >
void __make_heap_myself( RandomAccessIterator first ,  RandomAccessIterator last)
{
        for (RandomAccessIterator i = first +1 ; i < last ; ++i)
                __push_heap_myself(first , i);
}


//堆排序 直接利用出堆把大元素放在后续位置 即可 注意起始位置是last -1
template<class RandomAccessIterator >
void __sort_heap_myself( RandomAccessIterator first ,  RandomAccessIterator last)
{
        for (RandomAccessIterator i = last-1 ; i > first ; --i)
                __pop_heap_myself(first , i);
}

//把last位置的元素压入前面的堆 向上调整
template<class RandomAccessIterator>
void __push_heap_myself(RandomAccessIterator first,RandomAccessIterator last)
{
        if ( first == last )
                return ;
        typedef typename iterator_traits<RandomAccessIterator>::difference_type distance;
        typedef typename iterator_traits<RandomAccessIterator>::value_type T;
        T temp = *last ;
        distance des = last -first ;
        distance parent = ( des-1 ) / 2;
        while ( parent >  0)
        {
                if ( *(first + parent) >= temp )
                        {
                        *(first + des) = temp;
                        return;
                        }
                else
                        {
                        *(first + des) = *(first + parent);
                        des  = parent ;
                        parent = ( des -1 ) / 2;
                        }
        }
//最后一个元素的比较
        if(*first >= temp)
        {
                *(first +des) = temp;
        }
        else
        {
        *(first + des) = *first ;
        *first = temp ;
        }
}

//出堆函数,元素出堆到last上 先交换,在调整 第一个元素必然是最大的
template<class RandomAccessIterator>
void __pop_heap_myself(RandomAccessIterator first,RandomAccessIterator last)
{
        iter_swap(first , last);
        __adjust_heap_myself(first ,last,distance_type(first),value_type(first));
}

//用于pop的调整函数  向下调整
template<class RandomAccessIterator , class T ,class Distance>
void __adjust_heap_myself(RandomAccessIterator first,RandomAccessIterator last,Distance * ,T*)
{
        T temp = *first;

        Distance len = last - first ;
        Distance des = 0 ;
        while( true )
        {
                Distance left_pos = 2 * des + 1;
                Distance right_pos = 2 * des + 2;
                //有左右节点
                if( right_pos  < len)
                {
                //左边节点最大
                        if((*(first + left_pos) >= temp) && (*(first + left_pos)>=  *(first + right_pos)) )
                        {
                                *(first + des) = *(first + left_pos );
                                des = left_pos;
                        }
                //右边节点最大
                        else if((*(first + right_pos) >=  temp) && (*(first + right_pos)>=  *(first + left_pos)) )
                        {
                                *(first + des) = *(first + right_pos );
                                des = right_pos;
                        }

                //元素本来最大  
                        else //if(temp > *(first + 2*des +1) && temp > *(first + 2*des +2) )
                        {
                                *(first + des ) = temp;
                                return;
                        }
                }
                //只有左子节点
                else if( right_pos == len)
                {
                        if(temp > *(first + left_pos))
                                *(first + des) = temp;
                        else
                        {
                                *(first + des) = *(first + left_pos);
                                *(first + left_pos) = temp;
                        }
                        return;
                }
                //无左右节点
                if(right_pos > len)
                {
                        *(first + des) = temp ;
                        return;
                }

        }
}
ps:切记里面的大于等于 ,条件判断十分重要,如果没有等于,就会忽略一种情况,就是左右节点相同 比来比去就直接进入else了


目前为止,一些基本排序的实现已经列出了,排序比较总结
稳定排序   : 冒泡 , 插入 ,凡是o(n^2)的排序都是稳定的
快速,堆,归并则不稳定。
上面3个时间复杂度均值相同,但是快速排序会出现恶化分隔的情况,而归并排序需要辅助空间,以及成员的构造和赋值。