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

Sort a vector of integers based on the even values

本文关键字:向量 排序 整数      更新时间:2023-10-16

我有一个整数向量,元素数量为偶数。位置 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);
});

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

这样做的方法之一,假设你绝对不能修改你存储数据的方式,并且没有心情编写自己的排序例程,就是使用可怕的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快,有时非常明显。

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

想法

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

特别是,需要以下事项:

  • 符合RandomAccessIterator要求的迭代器类

  • 底层迭代器的包装器,它作为我们迭代器的"值" - 我称之为iter_wrapper

  • swapiter_wrapper专业化

  • 迭代器的std::iterator_traits专业化

我冒昧地将这个问题推广到任何兼容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实现测试了这段代码:fromlibc++(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. 更重要的是 - 也许有人有一些想法来改进这一点并使可行?也许我错过了什么?

我将尝试再次查看此内容,并在明天使用MSVC进行测试,届时我将有更多时间。如果有人在这种方法中穿会更新这篇文章。