我有一个带有偶数元素的整数向量。位置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);
});
编辑:我还应该提到我无法将矢量更改为对矢量。总是可以将内容复制到对的向量中,但我对使用较少内存的解决方案感兴趣。
答案 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::sort
比std::qsort
更快,有时非常明显。
答案 1 :(得分:1)
我尝试使用std::sort
和自定义迭代器类型解决此问题,该类型包装std::vector::iterator
(或其他标准容器的迭代器)。这涉及重新实现随机访问迭代器,这是
非常长,并且存在兼容性问题(见下文)。
<强>观强>
std::sort
的Documentation指定了可用作其参数的迭代器的要求。 从理论上讲,只需要编写一个符合这些要求的迭代器类,而不是 一次交换一个元素,交换2个(或通常为N个)元素。
特别需要做以下事情:
符合RandomAccessIterator
要求的迭代器类
底层迭代器的包装器,它作为我们的迭代器的“值” - 我称之为iter_wrapper
swap
specialization iter_wrapper
我冒昧地将此问题推广到任何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
) - 这还不够:(
然而,我想:
对其他人来说可能很有意思
更重要的是 - 也许某人有一些想法可以改善这一点并使其可行?也许我错过了什么?
我会再试一次,明天再试MSVC,那时我会有更多的时间。如果有人在这种方法中交叉,我会更新这篇文章。