是否可以对像std::sort
这样的迭代器定义的列表部分(列表的子集)进行排序?
即使用std::list
唯一可用的排序是通过方法(http://en.cppreference.com/w/cpp/container/list/sort),我希望能够使用std::sort
从其迭代器中对列表的一部分进行排序。
e.g
std::sort(listItrStart, listItrEnd, [](T& a, T& b){ return a.something() < b.something()});
我理解一旦对项目执行移动操作,迭代器将变为无效,我认为这意味着列表无法按迭代器排序,而无需在下一次比较之前重新迭代到所需位置&#39 ;
在哪种情况下,排序列表的子集的最佳做法是什么,而不为此过程填充另一个容器(如果有的话)?
非常感谢。
答案 0 :(得分:25)
填充另一个容器是不可避免的。但您不必移动或复制任何自己的数据。您可以使用std::list::splice
提取并重新插入要处理的节点到排序顺序。
using list_t = std::list<widget>;
void process(list_t& in, list_t::const_iterator begin, list_t::const_iterator end) {
list_t sorter;
sorter.splice(sorter.end(), in, begin, end);
sorter.sort();
in.splice(end, sorter);
}
该函数将您要排序的节点传输到排序器列表中(第一个迭代器参数是插入节点的位置,在本例中是列表的末尾)。
排序器列表(显然)已经排序,然后排序后的内容将被传输回源列表,完全转移到最初填充的原始子范围内。
由@ T.C评论。下一步是概括它。它可以像这样一样制作成模板:
template<class List, class Compare = std::less<>>
void sort_subrange(List& in,
typename List::const_iterator begin,
typename List::const_iterator end,
Compare c = {}) {
List sorter(in.get_allocator());
sorter.splice(sorter.end(), in, begin, end);
[[maybe_unused]] ScopeGuard sg([&]() { in.splice(end, sorter); });
sorter.sort(std::move(c));
}
此处也将比较器作为参数,sorter
由输入分配器的副本构成,以实现最大通用性。拼接在我们选择的范围内进行,以支持比较函数抛出的情况,因此我们的基础现在已被覆盖。
Here is a live example,出于展示目的,有一个天真的,有些愚蠢的范围保护实现。
答案 1 :(得分:0)
是否可以对由std :: sort等迭代器定义的列表(列表子集)的部分进行排序?
我假设你的意思是std :: list :: sort。 Visual Studio 2015的实现可以做到这一点,而无需填充另一个容器。它是一种自上而下的合并排序,效率低于之前的自下而上合并排序,但它避免了分配先前版本所做的内存,因为先前版本分配了一小部分列表。 Psuedo代码看起来像这样:
right = std::next(right, 1); // right = end of sub-list
size = std::distance(left, right);
left = MyListSort(list, left, right, size);
right = std::next(left, size-1); // right = last element of sub-list
// ...
MyListSort(list, left, right, size)
{
if(size < 2)
return left;
mid = std::next(left, size/2);
MyListSort(list, left, mid, size/2);
MyListSort(list, mid, right, size-size/2);
firstloop = true;
newleft = left;
while(true){
if(*left <= *mid){
if(++left == mid)
return newleft;
} else {
if(firstloop)
newleft = mid;
list.splice(left, list, mid);
if(++mid == right)
return newleft;
}
firstloop = false;
}
}