根据偶数值对整数向量进行排序

时间:2018-06-18 19:32:09

标签: c++

我有一个带有偶数元素的整数向量。位置0和1处的元素属于一起,位置2和3处的元素相同,等等......

我想基于偶数元素对此向量进行排序。奇数元素应与其对应的偶数元素保持在一起。我不想使用boost,但我想使用std :: sort。

例:
输入= {4,40,5,50,3,30,2,20,1,10}
输出= {1,10,2,20,3,30,4,40,5,50}

我提出了以下解决方案。这样安全吗?有更好的解决方案吗?

std::vector<int> values = {4,40,5,50,3,30,2,20,1,10};

auto begin = reinterpret_cast<std::array<int, 2>*>(values.data());
auto end = reinterpret_cast<std::array<int, 2>*>(values.data() + values.size());

std::sort(begin, end, [](const std::array<int, 2>& a, const std::array<int, 2>& b)
{
    return std::get<0>(a) < std::get<0>(b);
});

编辑:我还应该提到我无法将矢量更改为对矢量。总是可以将内容复制到对的向量中,但我对使用较少内存的解决方案感兴趣。

2 个答案:

答案 0 :(得分:5)

这样做的另一种方法是假设您绝对无法修改数据的存储方式,并且没有心情编写自己的排序例程,那就是使用可怕的std::qsort

在你的例子中,这将是(感谢@chux发现可能的溢出,感谢@JeJo注意到不正确的大小):

std::vector<int> values = {4,40,5,50,3,30,2,20,1,10};
std::qsort(values.data(), values.size() / 2, sizeof(values[0]) * 2, [](const void* a, const void* b) {
   const int* a_1 = static_cast<const int*>(a);
   const int* b_1 = static_cast<const int*>(b);
   return (*a_1 > *b_1) - (*a_1 < *b_1);
});

请注意,已知std::sortstd::qsort更快,有时非常明显。

答案 1 :(得分:1)

我尝试使用std::sort和自定义迭代器类型解决此问题,该类型包装std::vector::iterator (或其他标准容器的迭代器)。这涉及重新实现随机访问迭代器,这是 非常长,并且存在兼容性问题(见下文)。

<强>观

std::sort

Documentation指定了可用作其参数的迭代器的要求。 从理论上讲,只需要编写一个符合这些要求的迭代器类,而不是 一次交换一个元素,交换2个(或通常为N个)元素。

特别需要做以下事情:

我冒昧地将此问题推广到任何std::sort兼容的容器和N迭代器序列(而不是2)。

我到达了以下实现(示例使用在底部):

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

template<typename IteratorType, std::size_t Count>
struct multi_iterator
{    
    struct iter_wrapper
    {
        IteratorType first_iter;

        friend void swap(iter_wrapper a, iter_wrapper b)
        {
            for(std::size_t i = 0; i < Count; ++i)
            {
                using std::swap;
                swap(*(a.first_iter + i), *(b.first_iter + i));
            }
        }
    };

    IteratorType first_iter;

    explicit multi_iterator(const IteratorType& first_iter)
        : first_iter(first_iter)
    {}

    iter_wrapper operator *() { return {first_iter}; }

    multi_iterator operator +(std::ptrdiff_t n) const
    { return multi_iterator(first_iter + n * Count); }

    multi_iterator operator -(std::ptrdiff_t n) const
    { return multi_iterator(first_iter - n * Count); }

    std::ptrdiff_t operator -(const multi_iterator& other) const
    { return (first_iter - other.first_iter) / Count; }

    multi_iterator& operator +=(std::ptrdiff_t n)
    {
        first_iter += n * Count;
        return *this;
    }

    multi_iterator& operator -=(std::ptrdiff_t n)
    {
        first_iter -= n * Count;
        return *this;
    }

    multi_iterator& operator ++()
    {
        first_iter += Count;
        return *this;
    }

    multi_iterator& operator --()
    {
        first_iter -= Count;
        return *this;
    }

    bool operator <(const multi_iterator& other) const
    { return first_iter < other.first_iter; }

    bool operator >(const multi_iterator& other) const
    { return first_iter >= other.first_iter; }

    bool operator >=(const multi_iterator& other) const
    { return first_iter >= other.first_iter; }

    bool operator <=(const multi_iterator& other) const
    { return first_iter <= other.first_iter; }

    bool operator ==(const multi_iterator& other) const
    { return first_iter == other.first_iter; }

    bool operator !=(const multi_iterator& other) const
    { return first_iter != other.first_iter; }
};

namespace std
{
    template<typename IteratorType, std::size_t Count>
    struct iterator_traits<multi_iterator<IteratorType, Count>>
    {
        using value_type = typename multi_iterator<IteratorType, Count>::iter_wrapper;
        using reference = typename multi_iterator<IteratorType, Count>::iter_wrapper&;
        using pointer = typename multi_iterator<IteratorType, Count>::iter_wrapper*;
        using difference_type = std::ptrdiff_t;
        using iterator_category = std::random_access_iterator_tag;
    };
}

template<typename Type>
void print(const std::vector<Type>& v)
{
    std::cout << "[";

    for(std::size_t i = 0; i < v.size(); ++i)
    {
        if(i > 0) std::cout << ", ";
        std::cout << v[i];   
    }

    std::cout << "]\n";
}

int main()
{
    std::vector<int> values {7, 6, 2, 1, 5, 4, 10, 9};

    std::cout << "before: ";
    print(values);

    using iter_type = multi_iterator<std::vector<int>::iterator, 2>;

    std::sort(iter_type(values.begin()), iter_type(values.end()),
        [](const iter_type::iter_wrapper& a, const iter_type::iter_wrapper& b)
            { return *a.first_iter < *b.first_iter; });

    std::cout << "after: ";
    print(values);

    return 0;
}

<强>结果

我已经使用两个std::sort实现测试了此代码:来自libc++(clang的一个)和libstdc++(gcc的一个)。

使用libc++,此实现按预期工作。输入[7, 6, 2, 1, 5, 4, 10, 9],输出 [2, 1, 5, 4, 7, 6, 10, 9]

使用libstdc++它不起作用。看来图书馆不尊重我们的自定义sort重载 - 相反, 它试图直接移动multi_iterator::operator *()的结果。这打破了实现,因为它需要自定义移动/交换实现。

摘要(暂时)

我想演示如何使用std::sort和自定义迭代器类型来解决OP的问题。它部分可用(给定 正确实施sort) - 这还不够:(

然而,我想:

  1. 对其他人来说可能很有意思

  2. 更重要的是 - 也许某人有一些想法可以改善这一点并使其可行?也许我错过了什么?

  3. 我会再试一次,明天再试MSVC,那时我会有更多的时间。如果有人在这种方法中交叉,我会更新这篇文章。

相关问题