it-swarm.com.de

Sortieren von std :: list mit Iteratoren

Ist es möglich, einen Teil einer Liste (Teilmenge einer Liste) zu sortieren, die von Iteratoren wie std::sort definiert wird?

dh mit einem std::list steht nur eine Methode zur Verfügung ( http://en.cppreference.com/w/cpp/container/list/sort ), ich möchte einen Teil einer Liste sortieren können von seinen Iteratoren, die std::sort..__ verwenden

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

Ich weiß es zu schätzen, dass Iteratoren ungültig werden würden, sobald ein Verschiebevorgang für ein Element ausgeführt wird. Dies bedeutet, dass eine Liste nicht nach Iteratoren sortiert werden kann, ohne dass vor dem nächsten "Vergleichen" der gewünschte Ort erneut durchlaufen wird.

In welchem ​​Fall empfiehlt es sich, Listen von Listen zu sortieren, ohne einen anderen Container für diesen Prozess aufzufüllen (falls vorhanden)?

Danke vielmals.

23
lfgtm

Das Auffüllen eines anderen Containers ist unvermeidlich. Sie müssen jedoch keine eigenen Daten verschieben oder kopieren. Sie können std::list::splice verwenden, um die zu bearbeitenden Knoten in sortierter Reihenfolge zu extrahieren und erneut einzufügen.

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

Die Funktion überträgt die zu sortierenden Knoten in die Sortiererliste (das erste Iterator-Argument ist die Position, vor der die Knoten eingefügt werden, in diesem Fall das Ende der Liste).

Die Sortiererliste wird (offensichtlich) sortiert, und anschließend wird der sortierte Inhalt genau in den ursprünglichen Unterbereich, den er ursprünglich gefüllt hat, zurück in die Quellliste übertragen.


Wie von @ T.C kommentiert. Der nächste Schritt ist die Verallgemeinerung. Es kann ähnlich wie dieses in eine Vorlage umgewandelt werden:

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

Der Komparator wird auch hier als Argument verwendet, und sorter wird mit einer Kopie des Zuweisers der Eingabe für maximale Generizität erstellt. Das Zurückspleißen erfolgt in einem Bereichswächter unserer Wahl, um den Fall zu unterstützen, in dem die Vergleichsfunktion ausgelöst wird, sodass unsere Basen jetzt abgedeckt sind. 

Hier ist ein Live-Beispiel , mit einer naiven und etwas dummen Implementierung eines Scope Guard, zu Darstellungszwecken.

25
StoryTeller

Ist es möglich, einen Teil einer Liste (Teilmenge einer Liste) zu sortieren, die von Iteratoren definiert wird, wie dies bei std :: sort do der Fall ist?

Ich nehme an, Sie meinten std :: list :: sort. Die Implementierung von Visual Studio 2015 führt dies aus, ohne dass ein anderer Container gefüllt werden muss. Es handelt sich hierbei um eine Sortierung nach oben und unten, die weniger effizient ist als die vorherige Sortierung nach Bottom-Up-Typ. Sie vermeidet jedoch die Zuweisung von Speicherplatz, der von der vorherigen Version übernommen wurde, seit der vorherigen Version ein kleines Array von Listen zugewiesen wurde. Der Pseudo-Code würde ungefähr so ​​aussehen:

    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