web-dev-qa-db-fra.com

Tri d'une liste std :: list à l'aide d'itérateurs

Est-il possible de trier une partie d'une liste (sous-ensemble d'une liste) définie par des itérateurs comme le fait std::sort?

c'est-à-dire qu'avec un std::list le seul tri disponible est via une méthode ( http://fr.cppreference.com/w/cpp/container/list/sort ), j'aimerais pouvoir trier une partie d'une liste de ses itérateurs utilisant std::sort. par exemple

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

Je suis conscient que les itérateurs deviendraient invalides une fois l’opération de déplacement effectuée sur un élément, ce qui, je suppose, signifie qu’une liste ne peut pas être triée par itérateurs sans une nouvelle itération à l’emplacement souhaité avant la prochaine comparaison?

Dans quel cas, quelle est la meilleure pratique pour trier les sous-demandes de listes sans remplir un autre conteneur pour ce processus (le cas échéant)?

Merci beaucoup.

23
lfgtm

Remplir un autre conteneur est inévitable. Mais vous n'avez pas à déplacer ni copier aucune de vos propres données. Vous pouvez utiliser std::list::splice pour extraire et réinsérer les nœuds que vous souhaitez traiter dans un ordre trié.

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

La fonction transfère les noeuds que vous souhaitez trier dans la liste de tri (le premier argument d'itérateur est la position avant laquelle les noeuds sont insérés, dans ce cas la fin de la liste).

La liste de trieurs est triée (évidemment), puis le contenu trié est transféré dans la liste source, exactement dans la sous-plage d'origine remplie à l'origine.


Comme commenté par @ T.C. La prochaine étape consiste à le généraliser. Il peut être transformé en un modèle très semblable à celui-ci:

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

Le comparateur est également considéré comme un argument et sorter est construit avec une copie de l'allocateur de l'entrée pour une généricité maximale. L'épissure est effectuée dans un garde-champ de notre choix pour prendre en charge le cas où la fonction de comparaison est lancée, nos bases sont donc couvertes. 

Voici un exemple concret , avec une implémentation naïve et un peu ridicule de la portée, à des fins d’exposition.

25
StoryTeller

Est-il possible de trier une partie d'une liste (sous-ensemble d'une liste) définie par des itérateurs comme le fait std :: sort?

Je suppose que vous vouliez dire std :: list :: sort. C'est ce que implémente Visual Studio 2015 sans avoir à renseigner un autre conteneur. Il s'agit d'un tri par fusion descendant moins efficace que le tri par fusion précédent, mais il évite d'allouer de la mémoire comme le faisait la version précédente, car celle-ci avait alloué un petit tableau de listes. Le code Psuedo ressemblerait à ceci:

    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;
        }
    }
0
rcgldr