it-swarm.com.de

wie fügen Sie den Wert in einen sortierten Vektor ein?

ALLES,

Diese Frage ist eine Fortsetzung von dieser ..__ Ich denke, dass STL diese Funktionalität vermisst, aber es ist nur mein IMHO.

Nun zur Frage.

Betrachten Sie den folgenden Code:

class Foo
{
public:
    Foo();
...........
private:
    int paramA, paramB;
    std::string name;
};

int main()
{
    std::vector<Foo> foo;
    Sorter sorter;
    sorter.paramSorter = 0;
    std::sort( foo.begin(), foo.end(), sorter );
}

struct Sorter
{
    bool operator()(const Foo &foo1, const Foo &foo2)
    {
         switch( paramSorter )
         {
             case 0:
                 return foo1.name < foo2.name;
             case 1:
                 return foo1.paramA < foo2.paramB;
             case 2:
                 return foo1. paramA > foo2.paramB;
         }
    }
private:
    int paramSorter;
}

Zu jedem Zeitpunkt kann der Vektor umsortiert werden .. Die Klasse verfügt auch über die Getter-Methoden, die in der Sortiererstruktur verwendet werden.

Was wäre der effizienteste Weg, um ein neues Element in den Vektor einzufügen?

Situation, die ich habe, ist:

Ich habe ein Raster (Tabellenkalkulation), das den sortierten Vektor einer Klasse verwendet. Der Vektor kann jederzeit neu sortiert werden, und das Raster zeigt die sortierten Daten entsprechend an.

Jetzt muss ich ein neues Element in den Vektor/das Raster einfügen . Ich kann das gesamte Raster einfügen, neu sortieren und dann wieder anzeigen, aber dies ist insbesondere für das große Raster sehr ineffizient.

Jede Hilfe wäre dankbar.

32
Igor

Die einfache Antwort auf die Frage:

template< typename T >
typename std::vector<T>::iterator 
   insert_sorted( std::vector<T> & vec, T const& item )
{
    return vec.insert
        ( 
            std::upper_bound( vec.begin(), vec.end(), item ),
            item 
        );
}

Version mit einem Prädikat.

template< typename T, typename Pred >
typename std::vector<T>::iterator
    insert_sorted( std::vector<T> & vec, T const& item, Pred pred )
{
    return vec.insert
        ( 
           std::upper_bound( vec.begin(), vec.end(), item, pred ),
           item 
        );
}

Dabei ist Pred ein streng geordnetes Prädikat für Typ T. 

Damit dies funktioniert, muss der Eingabevektor bereits in diesem Prädikat sortiert sein.

Die Komplexität dieser Vorgehensweise ist O(log N) für die upper_bound-Suche (finden Sie die Einfügeposition), aber bis zu O(N) für das Insert selbst.

Für eine bessere Komplexität können Sie std::set<T> verwenden, wenn keine Duplikate vorhanden sind, oder std::multiset<T>, wenn Duplikate vorhanden sein könnten. Diese behalten automatisch eine sortierte Reihenfolge bei, und Sie können auch hier ein eigenes Prädikat angeben.

Es gibt verschiedene andere Dinge, die Sie tun könnten, die komplexer sind, z. Verwalten Sie eine vector und eine set/multiset/sorted vector neu hinzugefügter Elemente und fügen Sie diese dann zusammen, wenn genug davon vorhanden sind. Jede Art von Iteration durch Ihre Sammlung muss durch beide Sammlungen laufen. 

Die Verwendung eines zweiten Vektors hat den Vorteil, dass Ihre Daten kompakt bleiben. Hier sind Ihre "neu hinzugefügten" Elemente vector relativ klein, so dass die Einfügezeit O(M) ist, wobei M die Größe dieses Vektors ist und möglicherweise praktischer ist als die O(N) des Einfügens in den großen Vektor. Die Zusammenführung wäre O(N+M), was besser ist als O(NM). Es würde jeweils eine nach der anderen eingefügt werden. Insgesamt wäre es also O(N+M) + O(M²), M-Elemente einzufügen und dann zusammenzuführen.

Sie würden den Einfügungsvektor wahrscheinlich auch auf seiner Kapazität belassen, so dass Sie mit dem Wachsen keine Neuzuordnungen mehr durchführen, sondern lediglich Elemente verschieben müssen.

50
CashCow

Wenn Sie den Vektor ständig sortieren müssen, sollten Sie zunächst überlegen, ob die Verwendung von std::set oder std::multiset Ihren Code nicht vereinfacht.

Wenn Sie wirklich einen sortierten Vektor benötigen und schnell ein Element einfügen möchten, aber nicht ständig ein Sortierkriterium erzwingen möchten, können Sie zuerst mit std::lower_bound() die Position in einem sortierten suchen Bereich, in dem das Element in logarithmischer Zeit eingefügt werden soll, verwenden Sie die Funktion insert() member von vector, um das Element an dieser Position einzufügen.

Wenn die Leistung ein Problem darstellt, ziehen Sie ein Benchmarking std::list vs. std::vector in Betracht. Bei kleinen Objekten ist bekannt, dass std::vector aufgrund einer höheren Cachetrefferrate schneller ist, die insert()-Operation selbst ist jedoch für Listen rechnerisch schneller (Elemente müssen nicht verschoben werden).

24
Andy Prowl

Nur eine Notiz, Sie können auch upper_bound verwenden, je nach Ihren Bedürfnissen. upper_bound wird sicherstellen, dass neue Einträge, die anderen entsprechen, am Ende ihrer Sequenz angezeigt werden. lower_bound wird sicherstellen, dass neue Einträge, die anderen entsprechen, am Anfang ihrer Sequenz erscheinen. Kann für bestimmte Implementierungen nützlich sein (vielleicht Klassen, die eine "Position" teilen können, aber nicht alle Details!)

Beide werden Ihnen versichern, dass der Vektor nach < Ergebnis der Elemente sortiert bleibt, obwohl das Einfügen in lower_bound das Verschieben weiterer Elemente bedeutet.

Beispiel:

insert 7 @ lower_bound of { 5, 7, 7, 9 } => { 5, *7*, 7, 7, 9 }
insert 7 @ upper_bound of { 5, 7, 7, 9 } => { 5, 7, 7, *7*, 9 }
8
Brian Rodriguez

Anstatt einzufügen und zu sortieren. Sie sollten eine Suche durchführen und dann einfügen

Halten Sie den Vektor sortiert. (einmal sortieren). Wenn Sie einfügen müssen 

  1. finden Sie das erste Element, das größer ist als das Element, das Sie einfügen möchten.

  2. Führen Sie kurz vor dieser Position einen Einsatz aus.

Auf diese Weise bleibt der Vektor sortiert.

Hier ist ein Beispiel, wie es geht.

start {} empty vector

insert 1 -> find first greater returns end() = 1 -> insert at 1 -> {1}
insert 5 -> find first greater returns end() = 2 -> insert at 2 -> {1,5}
insert 3 -> find first greater returns 2 -> insert at 2 -> {1,3,5}
insert 4 -> find first greater returns 3 -> insert at 3 -> {1,3,4,5}
1
user995502

Wenn Sie zwischen Sortierreihenfolgen wechseln möchten, können Sie mehrere Indexdatenstrukturen verwenden, von denen jede in sortierter Reihenfolge aufbewahrt wird (wahrscheinlich eine Art ausgeglichener Baum wie std :: map, der Sortierschlüssel auf Vektorindizes oder std abbildet :: Zum Speichern von Zeigern auf Ihre Objekte gesetzt - jedoch mit unterschiedlichen Vergleichsfunktionen.

Hier ist eine Bibliothek, die dies tut: http://www.boost.org/doc/libs/1_53_0/libs/multi_index/doc/index.html

Bei jeder Änderung (Einfügen neuer Elemente oder Aktualisierung von Schlüsseln) müssen Sie alle Indexdatenstrukturen aktualisieren oder als ungültig kennzeichnen.

Dies funktioniert, wenn es nicht "zu viele" Sortierreihenfolgen gibt und nicht "zu viele" Aktualisierungen Ihrer Datenstruktur. Ansonsten - Pech, müssen Sie jedes Mal neu sortieren, wenn Sie die Reihenfolge ändern möchten.

Mit anderen Worten: Je mehr Indizes Sie benötigen (um Suchvorgänge zu beschleunigen), desto mehr Zeit benötigen Sie für Aktualisierungsvorgänge. Und natürlich braucht jeder Index Speicher.

Um die Anzahl der Indizes klein zu halten, können Sie eine Abfrage-Engine verwenden, die die Indizes mehrerer Felder kombiniert, um komplexere Sortierreihenfolgen über mehrere Felder hinweg zu unterstützen. Wie ein SQL-Abfrageoptimierer. Aber das kann übertrieben sein ...

Beispiel: Wenn Sie zwei Felder, a und b, haben, können Sie 4 Sortierreihenfolgen unterstützen:

  1. ein
  2. b
  3. erst a dann b
  4. erst b dann a

mit 2 Indizes (3. und 4.) . Mit mehr Feldern werden die möglichen Kombinationen von Sortierreihenfolgen schnell groß. Sie können jedoch immer noch einen Index verwenden, der "fast beliebig sortiert" sortiert wird, und während der Abfrage die verbleibenden Felder sortieren, die Sie nicht mit diesem Index erfassen konnten. Für die sortierte Ausgabe der gesamten Daten hilft das nicht viel. Wenn Sie jedoch nur nach einigen Elementen suchen möchten, kann das erste "Eingrenzen" viel helfen.

0
Sebastian