给定两个容器:std::list< int > a;
和std::list< int > b;
, - a.size() == b.size()
。需要同步对容器a
和b
进行排序,即a
中元素的每次交换都应该导致交换b
中的对应元素(位置索引意义上的对应关系)。假设a
和b
中的元素非常重要。即你不能复制它。
理想的 STL 是什么?如何使用std::sort
执行操作?如果a
为const
,该怎么办?
我目前所做的事情:
#include <iostream>
#include <iomanip>
#include <type_traits>
#include <utility>
#include <iterator>
#include <algorithm>
#include <list>
#include <vector>
#include <cstdlib>
#include <cassert>
template< typename first, typename second >
void
sort_synchronously(first & f, second & s)
{
std::size_t sz = f.size();
assert(sz == s.size());
struct P
{
typename first::iterator pfirst;
typename second::iterator psecond;
bool operator < (P const & p) const { return (*pfirst < *p.pfirst); }
void swap(P & p) noexcept { std::iter_swap(pfirst, p.pfirst); std::swap(pfirst, p.pfirst); std::iter_swap(psecond, p.psecond); std::swap(psecond, p.psecond); }
};
std::vector< P > p;
p.reserve(sz); // O(N) additional memory
auto fi = std::begin(f);
auto si = std::begin(s);
for (std::size_t i = 0; i < sz; ++i) {
p.push_back({fi, si});
++fi;
++si;
}
std::sort(std::begin(p), std::end(p)); // O(N * log N) time
}
int
main()
{
std::list< int > a{5, 4, 3, 2, 1};
std::list< int > b{1, 2, 3, 4, 5};
std::copy(std::cbegin(a), std::cend(a), std::ostream_iterator< int >(std::cout, " ")); std::cout << std::endl;
std::copy(std::cbegin(b), std::cend(b), std::ostream_iterator< int >(std::cout, " ")); std::cout << std::endl;
sort_synchronously(a, b);
std::copy(std::cbegin(a), std::cend(a), std::ostream_iterator< int >(std::cout, " ")); std::cout << std::endl;
std::copy(std::cbegin(b), std::cend(b), std::ostream_iterator< int >(std::cout, " ")); std::cout << std::endl;
return EXIT_SUCCESS;
}
但我无法为swap
提供免费的P::swap
(基于struct P
)功能。它是不可避免的语言限制(我不能在函数范围内定义非lambda函数,但可以定义非模板类)?
我发现swap
自由函数重载的存在不是std::sort
函数的类型要求。只需 MoveConstructible 和 MoveAssignable 即可。因此the code更合适(但仍然不完整)。存在一个非常棘手的问题:提供给std::sort
的范围内的元素交换(显然)被分成一系列的合并操作:T tmp(std::move(lhs)); lhs = std::move(rhs); rhs = std::move(tmp);
。因此,我不能交换(在std::sort
期间)容器本身的引用元素,而只交换它们的迭代器。
答案 0 :(得分:2)
完美的STL方式是用vector
填充std::pair
并创建custom comparator,只比较对中的第一个元素。然后你将对对的矢量进行排序。
答案 1 :(得分:2)
正确的方法是使用std::pair<T1 &, T2 &>
之类的value_type
创建一个迭代器类。它可能应该在每个要排序的序列上包含一个迭代器,并正确地将操作传播给它们。
事实上,这正是boost::zip_iterator所做的。我建议使用适当的比较器;或者至少使用boost::zip_iterator
作为它应该如何运作的一个例子。
答案 2 :(得分:2)
一个相当简单的解决方案是在列表中构建迭代器的向量v
,并对其进行排序。然后,v
的第i个元素指向列表中应该占据排序列表中第i个位置的元素,您可以重建它们。由于使用了辅助容器,性能可能不是最佳的,但它很容易理解。
void ZippedSort(std::list<A>& a, std::list<B>& b) {
using PairOfIts = pair<decltype(a.begin()), decltype(b.begin())>;
vector<PairOfIts> v;
auto i = a.begin();
auto j = b.begin();
for (; i != a.end(); ++i, ++j)
v.push_back(make_pair(i, j));
std::sort(v.begin(), v.end(), [](PairOfIts const& i, PairOfIts const& j) { return *i.first < *j.first; } );
list<A> sortedA;
list<B> sortedB;
for (auto& x : v) {
sortedA.splice(sortedA.end(), a, x.first);
sortedB.splice(sortedB.end(), b, x.second);
}
swap(sortedA, a);
swap(sortedB, b);
}
答案 3 :(得分:0)
好的,完成了。但看起来(不是太脏)hack:在T tmp(std::move(lhs)); lhs = std::move(rhs); rhs = std::move(tmp);
std::swap
实现链中我使std::sort
算法只执行中间操作(其他都是无操作):
#include <iostream>
#include <iomanip>
#include <type_traits>
#include <utility>
#include <iterator>
#include <algorithm>
#include <vector>
#include <forward_list>
#include <cstdlib>
#include <cassert>
template< typename first, typename second >
void
sort_synchronously(first & f, second & s)
{
std::size_t sz = static_cast< std::size_t >(std::distance(std::cbegin(f), std::cend(f)));
assert(sz == static_cast< std::size_t >(std::distance(std::cbegin(s), std::cend(s))));
struct P
{
typename first::iterator pfirst;
typename second::iterator psecond;
bool signal;
bool operator < (P const & p) const { return (*pfirst < *p.pfirst); }
P(typename first::iterator pf, typename second::iterator ps)
: pfirst(pf)
, psecond(ps)
, signal(false)
{ ; }
P(P &&) : signal(true) { ; }
void operator = (P && p) { if (!p.signal) { std::iter_swap(pfirst, p.pfirst); std::iter_swap(psecond, p.psecond); } }
};
std::vector< P > p;
p.reserve(sz);
auto fi = std::begin(f);
auto si = std::begin(s);
for (std::size_t i = 0; i < sz; ++i) {
p.emplace_back(fi, si);
++fi;
++si;
}
std::sort(std::begin(p), std::end(p));
}
int
main()
{
std::forward_list< int > a{5, 4, 3, 2, 1};
std::forward_list< int > b{10, 20, 30, 40, 50};
std::copy(std::cbegin(a), std::cend(a), std::ostream_iterator< int >(std::cout, " ")); std::cout << std::endl;
std::copy(std::cbegin(b), std::cend(b), std::ostream_iterator< int >(std::cout, " ")); std::cout << std::endl;
sort_synchronously(a, b);
std::cout << std::endl;
std::copy(std::cbegin(a), std::cend(a), std::ostream_iterator< int >(std::cout, " ")); std::cout << std::endl;
std::copy(std::cbegin(b), std::cend(b), std::ostream_iterator< int >(std::cout, " ")); std::cout << std::endl;
return EXIT_SUCCESS;
}
我确信static_assert(std::is_const< first >{});
的修改很明显(只需将typename first::iterator
更改为typename first::const_iterator
并执行std::swap(pfirst, p.pfirst);
而不是std::iter_swap(pfirst, p.pfirst);
。)