如何将比较器添加到自定义排序功能

时间:2018-03-28 12:38:53

标签: c++ sorting c++11 c++14 comparator

所以我已经实现了合并排序,出于所有目的和目的,它也可以是自定义排序功能,我已经开始将其转换为模板功能。

我遇到问题的地方是我希望增加传递自定义比较功能以便以不同方式排序的可能性。 (例如,std :: greater和std :: less或任何自定义的)。

我已经验证了排序算法在我用T替换整数时有效。如何从这里添加自定义比较函数以便对自定义对象进行排序等?

template <  typename T, 
            class Compare>
void merge( vector<T> &arr, int start, int mid, int end, Compare comp ) 
{
    int lptr = start; 
    int rptr = mid+1; 
    int tempptr = 0; 

    vector<T> temp( end - start + 1 ); 

    for ( int i = 0; i<temp.size(); i++)
    {
        if ( lptr > mid ) //done with left-section, just move the right elements
        {   
            temp[tempptr] = arr[rptr];
            rptr++;
        } else if ( rptr > end ) //done with right-section, just move the left elements
        {
            temp[tempptr] = arr[lptr];
            lptr++; 
        } else if ( comp( arr[rptr], arr[lptr] )) // right item < left item, move right item
        {
            temp[tempptr] = arr[rptr]; 
            rptr++; 
        } else          //otherwise left item < right item, move left item
        {
            temp[tempptr] = arr[lptr];
            lptr++; 
        }
        tempptr++;
    }

    for ( int i = 0; i<temp.size(); i++)
    {
        arr[start + i] = temp[i]; 
    }
}






template <  typename T, 
            class Compare>
void mergeSort( vector<T> &arr, int start, int end, Compare comp)
{   

    //if we're down to single elements, do nothing
    if ( start < end ){
        //call to right and left 'child' 
        int mid = (start + end) / 2; 

        mergeSort( arr, start, mid ); 
        mergeSort( arr, mid + 1, end );

        //call to merge
        merge( arr, start, mid, end ); 
    }
}



int main()
{   
    vector<float> arr = {7,8, 2, 6.6, 1, 4.1, 5, 3, 8, 9};
    cout << "before sorting:" << endl;
    for ( auto n : arr ) 
        cout << n << ", ";
    cout << endl;
    mergeSort( arr, 0, arr.size() - 1); 

    cout << "after sorting:" << endl;
    for ( auto n : arr ) 
        cout << n << ", ";
    cout << endl;

    return 0; 
};

提前致谢。

3 个答案:

答案 0 :(得分:1)

正如Sam Varshavchik所述,将比较运算符替换为比较函数。这意味着:

 if ( lptr > mid ) //done with left-section, just move the 

对此的更改:

       if ( comp(lptr,mid) ) //done with left-section, just move the 

顺便说一句,你有一个未经处理的案件:

template <  typename T, 
            class Compare>
void mergeSort( vector<T> &arr, int start, int end, Compare comp)
{   

    //if we're down to single elements, do nothing
    if ( start < end ){
        //call to right and left 'child' 
        int mid = (start + end) / 2; 

        mergeSort( arr, start, mid ); 
        mergeSort( arr, mid + 1, end );

        //call to merge
        merge( arr, start, mid, end ); 
    }
    else{ throw "Not handled case";}
}

答案 1 :(得分:0)

考虑到您有class or struct CustomType

Pre c ++ 11

struct CustomCompare
{
    bool operator ()(const CustomType& a, const CustomType& b)
    {
        return a.Watever < b.Watever;
    }
};

//usage
merge(vector<CustomType> ..., CustomCompare());

发布c ++ 11,using lambdas

auto CustomCompare = [](const CustomType & a,const CustomType& b)
{
    return a. .... ;
};
//usage
merge(vector<CustomType> ..., CustomCompare);

有第三种选择:

您可以使用std::less但必须存在以operator <作为参数的CustomType

示例:

struct CustomType
{
    //...
    bool operator < (const CustomType& other)const
    {
        return this->Whatever < other.Whatever;
    }
};

你可以专攻std::less

namespace std
{
    template <>
    struct less <CustomType>
    {
        bool operator()(const CustomType & a, const CustomType & b)
        {
            return ...
        }
    };
}

答案 2 :(得分:0)

我认为您对mergemerge sort的实施过度复杂化了。我已经使用2次重载编写了相同的函数并将它们放在命名空间中,这样它们就不会与std库的merge版本发生冲突。看看我的例子,看看已经做了什么。

#include <iostream>
#include <vector>
#include <algorithm>

namespace test {

// without comp predicate
template<class InputIt1, class InputIt2, class OutputIt>
OutputIt merge( InputIt1 first1, InputIt1 last1,
                InputIt2 first2, InputIt2 last2,
                OutputIt d_first ) {

    for( ; first1 != last1; ++d_first ) {
        if( first2 == last2 ) {
            return std::copy( first1, last1, d_first );
        }
        if( *first2 < *first1 ) {
            *d_first = *first2;
            ++first2;
        } else {
            *d_first = *first1;
            ++first1;
        }
    }
    return std::copy( first2, last2, d_first );
}

// with comp predicate
template<class InputIt1, class InputIt2, class OutputIt, class Compare>
OutputIt merge( InputIt1 first1, InputIt1 last1,
                InputIt2 first2, InputIt2 last2,
                OutputIt d_first, Compare comp ) {

    for( ; first1 != last1; ++d_first ) {
        if( first2 == last2 ) {
            return std::copy( first1, last1, d_first );
        }
        // This is were I replaced the default `< operator` with the `Compare` predicate.
        if( comp( *first2, *first1 ) ) {
            *d_first = *first2;
            ++first2;
        } else {
            *d_first = *first1;
            ++first1;
        }
    }
    return std::copy( first2, last2, d_first );
}

} // namespace test
int main() {
    std::vector<int> v1{ 1,3,5,7,9 };
    std::vector<int> v2{ 2,4,6,8 };
    std::vector<int> v3;

    // print this way
    std::cout << "v1 : ";
    for( auto& v : v1 ) {
        std::cout << v << ' ';
    }
    std::cout << '\n';

    // or print this way
    std::cout << "v2 : ";
    std::copy( v2.begin(), v2.end(), std::ostream_iterator<int>( std::cout, " " ) );
    std::cout << '\n';

    // Merge without binary predicate comp function - functor etc.
    test::merge( v1.begin(), v1.end(), 
                 v2.begin(), v2.end(), 
                 std::back_inserter( v3 ) );

    // using std's functors std::less - std::greater
    test::merge( v1.begin(), v1.end(), 
                 v2.begin(), v2.end(), 
                 std::back_inserter( v3 ), 
                 std::less<int>() );

    test::merge( v1.begin(), v1.end(), 
                 v2.begin(), v2.end(), 
                 std::back_inserter( v3 ), 
                 std::greater<int>() );

    // using lambda's as predicate compare objects.
    test::merge( v1.begin(), v1.end(), 
                 v2.begin(), v2.end(), 
                 std::back_inserter( v3 ), 
                 []( auto&& a, auto&& b ) { return a < b; } );

    test::merge( v1.begin(), v1.end(), 
                 v2.begin(), v2.end(), 
                 std::back_inserter( v3 ), 
                 []( auto&& a, auto&& b ) { return a > b; } );    

    std::cout << "v3 : ";
    std::copy( v3.begin(), v3.end(), std::ostream_iterator<int>( std::cout, " " ) );
    std::cout << '\n';    

    std::cout << "\nPress any key to quit.\n";
    std::cin.get();
    return 0;
}

这两个过载功能完全符合您的要求;合并和排序,同时能够选择在单个函数中使用什么谓词comp函数,仿函数等。

使用模板class InputIt表示法简化了函数的许多内部部分;无需跟踪sizesindex positionsindexing into arrays

我们真正需要做的就是使用Input Iterators上的相应比较运算符进行for循环,然后决定何时使用std::copy(...)或从first2分配元素或first1然后递增我们的iterator。最后,在for循环结束后,我们想要使用并返回std::copy(...)。默认情况下,没有比较谓词的第一个重载使用< operator,其中第二个重载采用谓词。

这也允许您传递具有beginenditerator的任何类型的容器,使其非常通用,模块化和便携,同时尝试保持最佳做法现代c ++。