Datenstruktur, die immer n-beste Elemente enthält

8

Ich brauche eine Datenstruktur, die immer die n größten Elemente enthält, die bisher eingefügt wurden (in keiner bestimmten Reihenfolge).

Wenn also n 3 ist, könnten wir die folgende Sitzung haben, in der ich einige Zahlen einfüge und den Inhalt des Containers ändert:

%Vor%

Sie bekommen die Idee. Wie heißt die Datenstruktur? Was ist der beste Weg dies zu implementieren? Oder ist das in einer Bibliothek?

Ich denke, einen Container mit einem priority_queue für seine Elemente (Delegierung) zu verwenden, der den umgekehrten Vergleich verwendet, so dass pop das kleinste Element entfernt. Die Funktion insert prüft zuerst, ob das neue einzufügende Element größer als das kleinste Element ist. Wenn das der Fall ist, werfen wir das kleinste heraus und drücken das neue Element.

(Ich habe eine C++ Implementierung im Hinterkopf, aber die Frage ist trotzdem sprachunabhängig.)

    
Frank 19.02.2009, 06:04
quelle

7 Antworten

12

Die gewünschte Datenstruktur ist wahrscheinlich der implizite Heap . Die rohe Datenstruktur ist nur ein Array; Nennen Sie für die Bequemlichkeit, dass es N = 2 ^ n Elemente in der Größe ist, und dass Sie die größten N-1 Elemente beibehalten möchten.

Die Idee ist, das Array (nennen wir es A) als kompletten binären Baum der Tiefe n zu behandeln:

  • ignoriere A [0]; behandle A [1] als Wurzelknoten
  • für jeden Knoten A [k] sind die Kinder A [2 * k] und A [2 * k + 1]
  • Knoten A [N / 2..N-1] sind die Blätter

Um den Baum als "Heap" zu erhalten, müssen Sie sicherstellen, dass jeder Knoten kleiner als (oder gleich) seinen Kindern ist. Dies wird "Heap-Bedingung" genannt:

  • A [k] & lt; A [2 * k]
  • A [k] & lt; = A [2 * k + 1]

So verwenden Sie den Heap, um die größten N-Elemente zu verwalten:

  • Beachten Sie, dass die Wurzel A [1] das kleinste Element im Heap ist.
  • vergleiche jedes neue Element (x) mit der Wurzel: Wenn es kleiner ist (x & lt; A [1]), lehne es ab.
  • Andernfalls fügen Sie das neue Element wie folgt in den Heap ein:
    • Entfernen Sie die Wurzel (A [1], das kleinste Element) aus dem Heap, und lehnen Sie sie ab
    • ersetze es durch das neue Element (A [1]: = x)
    • Stellen Sie jetzt die Heap-Bedingung wieder her:
      • Wenn x kleiner oder gleich seinen beiden Kindern ist, sind Sie fertig
      • andernfalls tauschen Sie x mit dem kleinsten Kind aus
      • Wiederholen Sie den Test und den Austausch an jeder neuen Position, bis die Heap-Bedingung erfüllt ist

Im Grunde wird jedes Ersatzelement den Baum "filtern", bis er seinen natürlichen Platz erreicht hat. Dies wird höchstens n = log2 (N) Schritte dauern, was so gut wie möglich ist. Außerdem ermöglicht die implizite Form des Baums eine sehr schnelle Implementierung; Bestehende Bibliotheken mit eingeschränkter Priorität werden höchstwahrscheinlich einen impliziten Heap verwenden.

    
comingstorm 19.02.2009, 06:50
quelle
4

Eine Prioritätswarteschlange ist in C ++ mit STL am nächsten. Sie könnten es in eine andere Klasse einfügen, um eine eigene Implementierung zu erstellen, die die Größe automatisch anpasst.

Sprach-agnostisch (obwohl vielleicht nicht Speicher-Fragmentierung-sicher):

  1. Daten einfügen
  2. Sortieren
  3. Lösche alles nach dem n-ten Element

std :: priority_queue macht Schritt 2 für Sie.

    
user64075 19.02.2009 06:15
quelle
4

In Java können Sie ein SortedSet verwenden, das z. von einem TreeSet. Prüfen Sie nach jedem Einfügen, ob die Menge zu groß ist. Wenn ja, entfernen Sie das letzte Element.

Dies ist einigermaßen effizient, ich habe es erfolgreich für die Lösung mehrerer Project Euler-Probleme verwendet.

    
starblue 19.02.2009 07:30
quelle
2

Eine beschränkte Prioritätswarteschlange , denke ich ... Java hat so etwas in seiner Standardbibliothek. BEARBEITEN : Es heißt LinkedBlockingQueue . Ich bin mir nicht sicher, ob die C ++ STL etwas ähnliches enthält.

    
David Z 19.02.2009 06:07
quelle
1

Ist es nicht möglich, die ersten n Elemente aus einer sortierten Sammlung zu übernehmen?

    
Rick 19.02.2009 07:30
quelle
0

Ja, Sie können einen minimalen Kopf der Größe N beibehalten Dann vergleichen Sie das neue Element mit dem Stammelement bei jedem Einfügen Pop die Wurzel und fügen Sie das Element ein, wenn es "größer" als die Wurzel ist Schließlich enden Sie mit N größten Items

    
user1206899 21.11.2012 07:46
quelle
-1

Erstellen Sie einen Min-Heap, speichern Sie auch einen Zähler.

Immer wenn der Zähler erreicht ist; Extrakt-min.

Sie können dies tun in: O (1) einfügen, get-min und O (log log n) extract-min. [1] Alternativ können Sie dies mit O (log n) einfügen und O (1) für die anderen erwähnten Operationen. [2]

[1] M. Thorup, "Integer-Prioritätswarteschlangen mit Verkleinerungsschlüssel in konstanter Zeit und das Problem mit den kürzesten Wegen der einzelnen Quelle", in Proceedings des fünfunddreißigsten jährlichen ACM-Symposiums über Theory of Computing, New York, NY, USA , 2003, S. 149-158.

C. Brodal, G. Lagogiannis, C. Makris, A. Tsakalidis und K. Tsichlas, "Optimale Fingersuchbäume in der Zeigermaschine", J. Comput. Syst. Sci., Vol. 67, nein. 2, pp. 381-418, Sep. 2003.

    
A T 05.01.2013 09:30
quelle