Effiziente Möglichkeit, eine std :: list zu iterieren, die sich ändert?

8

Ich versuche, eine std::list zu durchlaufen, aber es gibt ein Problem - die während der Iteration durchgeführten Operationen können dazu führen, dass ein Element aus der Liste hinzugefügt oder entfernt wird. Hinzufügungen sind in diesem Fall kein Problem, aber ein Entfernen kann dazu führen, dass alle Iteratoren in der Liste ungültig werden, einschließlich des aktuellen oder des nächsten Elements in der Sequenz.

Der Punkt, an dem die Entscheidung getroffen wird, die Liste zu modifizieren, ist weit entfernt von der Iterationsschleife - der Debugger zeigt 40 Funktionsaufrufe in dem Aufrufstapel zwischen den beiden. Aus diesem Grund ist es nicht möglich, den Iterator basierend auf dem Entfernen zu ändern.

Das Einzige, was mir einfällt, ist, am Anfang eine Kopie der Liste zu erstellen und darüber zu iterieren und jedes Element zu testen, um sicherzustellen, dass es immer noch in der Hauptliste ist. Das ist ein O (n ^ 2) Vorschlag, den ich möglichst vermeiden möchte.

    
Mark Ransom 10.06.2013, 22:44
quelle

4 Antworten

2

Sie haben drei Möglichkeiten:

  1. Wie Sie sagten, erstellen Sie eine lokale Kopie der Liste
  2. Wenn die Iteratoren ungültig sind, beginnen Sie erneut von vorn (und überspringen Sie vielleicht n iterations? [mit continue ])

  3. Bearbeiten: Wie Pubby sagte, markiere entfernte Elemente als abgelaufen, und wenn du Elemente hinzufügst, benutze das skipping n iterations Zeug:)

Iteratoren erlauben keinen Zugriff per Index, daher ist es etwas schwieriger, eine elegante Lösung für Ihr Problem zu finden.

    
user1182183 10.06.2013 22:48
quelle
1
  

kann dazu führen, dass ein Iterator in der Liste ungültig wird

Keine.

Sehen Sie sich die Beschreibung von std::list::erase :

an

Iterator-Gültigkeit

  • Iteratoren, Zeiger und Referenzen, die sich auf Elemente beziehen, die von entfernt wurden Die Funktion wird ungültig gemacht.
  • Alle anderen Iteratoren, Zeiger und Referenzen behalten ihre Gültigkeit

Fazit zum Löschen: Nur das entfernte Element wird ungültig. Alle anderen bleiben gültig. Und in diesem Fall haben Sie die Möglichkeit, O (n) in den meisten Kontexten zu implementieren.

    
Boris 10.06.2013 23:05
quelle
1

Hier sind einige weitere Optionen.

1. Splice

Anstatt ein Element zu entfernen, könnte "modifizierender" Code es in eine temporäre Liste verschieben mit splice Methode. Das Verbinden einzelner Elemente ist eine konstante Zeitoperation. Und es macht keine Iteratoren oder Referenzen ungültig (selbst für ein verschobenes Element).

Vor dem Verschieben eines Elements sollte "modifizierender" Code die Hauptkopie des Listen-Iterators (der zum "iterierenden" Code gehört) vorrücken.

"Iterating" Code sollte nur diese temporäre Liste nach jeder Iteration löschen.

Vorteile: Sie müssen den enthaltenen Elementen keine Flags hinzufügen.

Nachteile: einige Leistungseinbußen wegen der Notwendigkeit, temporäre Liste zu löschen; benötigte externe Schnittstelle, um den zu "iterierenden" Code gehörenden Iterator weiterzuentwickeln; Wenn ein Code zwischen "iterierenden" und "modifizierenden" Codes die nächsten / vorherigen Elemente relativ zum "entfernten" Element untersuchen muss, sieht er nur andere "entfernte" Elemente anstelle des Rests der Liste.

2. Splice mit "gesperrt" -Flag

Wenn Sie das Flag "locked" für das Element setzen, auf das der Iterator gerade zeigt, kann "Modifying" -Code Spleiß nur für dieses einzelne Element verwenden und andere auf die übliche Weise entfernen.

"Iterating" -Code sollte nur die temporäre Liste nach jeder Iteration löschen.

Vorteile: praktisch keine Leistung.

Nachteile: müssen Listenelemente ändern. benötigte externe Schnittstelle, um den zu "iterierenden" Code gehörenden Iterator weiterzuentwickeln; Wenn ein Code zwischen "iterierenden" und "modifizierenden" Codes die nächsten / vorherigen Elemente relativ zum "entfernten" Element überprüfen muss, findet er nichts.

3. "Gesperrt" und "entfernt" -Flags

Wenn Sie das Flag "locked" für das Element setzen, auf das iterator gerade zeigt, kann der "modifizierende" Code das Flag "removed" für dieses einzelne Element setzen und andere auf die übliche Weise entfernen.

"Iterating" -Code sollte (nach jeder Iteration) einfach ein Element entfernen, das zum Entfernen markiert ist.

Vorteile: praktisch keine Leistungseinbußen; Wenn ein Code zwischen "iterierenden" und "modifizierenden" Codes die nächsten / vorherigen Elemente relativ zum "entfernten" Element überprüfen muss, funktioniert er wie erwartet.

Nachteile: müssen Listenelemente ändern.

    
Evgeny Kluev 11.06.2013 10:40
quelle
0

Zwei mögliche Ansätze kommen in Betracht (zusätzlich zu einer Kopie der Liste):

1) Löschen Sie keine Sachen sofort aus der Liste. Erstellen Sie stattdessen einen weiteren Container mit Iteratoren, die gelöscht werden sollen, sobald Sie mit der vollständigen Iteration fertig sind. Dies ändert das Verhalten ("gelöschte" Elemente können immer noch bearbeitet werden), also weiß ich nicht, ob es Ihnen hilft.

2) Lassen Sie Ihre Liste entweder Zeiger oder ein Paar mit dem Item / Validity-Flag enthalten und löschen Sie einfach die Gültigkeit. Wenn Sie das erste Mal iteriert haben, führen Sie eine weitere Iteration durch, um die ausgefallenen Knoten zu bereinigen.

    
Mark B 11.06.2013 00:05
quelle

Tags und Links