简体   繁体   English

使用迭代器对std :: list进行排序

[英]Sorting a std::list using iterators

Is it possible to sort portion of a list (subset of a list) defined by iterators like std::sort does? 是否有可能对由std::sort这样的迭代器定义的列表(列表的子集)进行std::sort

ie with a std::list the only sort available is via a method ( http://en.cppreference.com/w/cpp/container/list/sort ), I would like to be able to sort part of a list from its iterators using std::sort . 即使用std::list ,唯一可用的排序是通过一种方法( http://en.cppreference.com/w/cpp/container/list/sort ),我希望能够对列表的一部分进行排序它的迭代器使用std::sort eg 例如

std::sort(listItrStart, listItrEnd, [](T& a, T& b){ return a.something() < b.something()});

I appreciate that iterators would become invalidated once a move operation is done on an item, which I assume means that a list cannot be sorted by iterators without re-iterating to the desired location before the next 'compare'? 我感谢一旦对项目执行了移动操作,迭代器将变为无效,我认为这意味着在下一次“比较”之前,迭代器无法对列表进行排序而无需重新迭代到所需位置?

In which case what is the best practice for sorting subsests of lists without populating another container for this process (if any)? 在这种情况下,排序子列表的最佳做法是什么,而不为此过程填充另一个容器(如果有的话)?

Many thanks. 非常感谢。

Populating another container is unavoidable. 填充另一个容器是不可避免的。 But you don't have to move or copy any of your own data. 但您不必移动或复制任何自己的数据。 You can use std::list::splice to extract and reinsert the nodes you want to process into sorted order. 您可以使用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);
}

The function transfers the nodes you wish to sort into the sorter list (the first iterator argument is the position before which the nodes are inserted, in this case the end of the list). 该函数将您要排序的节点传输到排序器列表中(第一个迭代器参数是插入节点的位置,在本例中是列表的结尾)。

The sorter list is sorted (obviously), and then the sorted content is transferred back into the source list, exactly into the original sub-range it originally populated. 排序器列表(显然)进行排序,然后将排序后的内容传输回源列表,完全转移到最初填充的原始子范围内。


As commented by @TC The next step is to generalize it. 由@TC评论下一步是概括它。 It can be made into a template much like this one: 它可以像这样制作成模板:

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));
}

The comparator is taken as an argument here as well, and sorter is constructed with a copy of the input's allocator for maximum genericity. 这里也将比较器作为参数,并使用输入分配器的副本构造sorter以获得最大的通用性。 The splicing back is done in a scope guard of our choosing to support the case where the compare function throws, so our bases are now covered. 拼接在我们选择的范围内进行,以支持比较函数抛出的情况,因此我们的基础现在已被覆盖。

Here is a live example , with a naive and somewhat silly implementation of a scope guard, for exposition purposes. 这是一个现实的例子 ,为了展示目的,有一个天真的,有点愚蠢的范围保护实现。

Is it possible to sort portion of a list (subset of a list) defined by iterators like std::sort does? 是否有可能对由std :: sort这样的迭代器定义的列表(列表的子集)进行排序?

I assume you meant std::list::sort. 我假设你的意思是std :: list :: sort。 Visual Studio 2015's implementation does this, and without having to populate another container. Visual Studio 2015的实现可以做到这一点,而无需填充另一个容器。 It is a top down merge sort that is less efficient than the prior bottom up merge sort, but it avoids allocating memory that the prior version did since the prior version allocated a small array of lists. 它是一种自上而下的合并排序,效率低于之前的自下而上合并排序,但它避免了分配先前版本所做的内存,因为先前版本分配了一小部分列表。 Psuedo code would look something like this: 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;
        }
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM