2009-05-06 8 views
2

Entsprechend dem Wikipedia-Artikel auf dynamic arrays, Einfügen/Löschen am Ende des Arrays ist O (1) beim Einfügen/Löschen von der Mitte ist O (n). Warum genau ist das?Warum ist das Einfügen am Ende eines dynamischen Arrays O (1) beim Einfügen in der Mitte O (n)?

Auch - wenn ich ein dynamisches Array mit 5 Elementen habe und ein neues Element an Position 6 einfügen, ist die Operation O (n), wenn ich die Funktion an das Ende des Arrays angehängt habe, ist O (1). Ist dies nicht dieselbe Operation, wenn das Array nicht in beiden Fällen neu skaliert werden muss? Oder fügt das Anfügen an das dynamische Array das neue Element wirklich neben Position 6 ein?

Danke!

EDIT: Ich denke, meine Haupt Verwirrung ist der Unterschied zwischen dem Einfügen am Ende des Arrays und Einfügen an einer bestimmten Position, die dem Ende des Arrays entspricht.

Ich nehme an, ein Zeiger auf die Speicheradresse des Endes des Arrays wird griffbereit gehalten, und deshalb ist die Append-Operation schnell. Umgekehrt, wenn ich eine genaue Position (selbst wenn es das Ende des Arrays ist) angeben, wird es nicht wissen, dass das Einfügen an dieser Position ist gleichbedeutend mit der oben genannten Speicheradresse, so dass es das gesamte Array durchlaufen muss, wie?

+0

Wie bei Ihrer Bearbeitung, hängt es wieder von der Implementierung ab. Sie könnten sehr gut ein dynamisches Array erstellen, das intelligent genug ist, um zu wissen, dass Insert (bound-1) dasselbe ist wie Add. –

+0

Hmm ... mir ist klar, dass ich hier in einem Beitrag 2 relativ unterschiedliche Fragen gestellt habe. Adam Robinson gab eine gute Antwort und Pax gab der anderen eine größere Antwort. Irgendein Konsens darüber, was die akzeptierte Antwort sein sollte ??? –

+0

Sie fragen eine voreingenommene Gruppe;) Wählen Sie die, die eine hilfreichere Antwort gibt. –

Antwort

8

Die Größenordnung würde vollständig davon abhängen, welche Art von Datenstruktur das "dynamische Array" tatsächlich ist ("dynamisches Array" ist keine streng definierte Datenstruktur, es ist eher ein erwünschtes Ergebnis, das durch die Verwendung bestimmter Daten erreicht wird) Struktur). Das Beispiel, das Sie angeben, wäre ein dynamisches Array, das durch Verwendung einer verketteten Liste erreicht wird. Das Hinzufügen zum Ende könnte O (1) sein, wenn die Listenstruktur einen Zeiger auf das letzte Element hält. Das Einfügen (unabhängig vom Index) würde das Durchlaufen der verknüpften Liste erfordern, was eine Operation pro Knoten bis zum gewünschten Index bedeutet.

+0

Das macht Sinn. Leider auf der Plattform, auf die ich mich in meinem Beispiel (UniData) beziehe, weiß ich nicht wirklich, wie das dynamische Array unter den Deckeln implementiert wird ... aber von dem, was du sagst, denke ich, dass es eine verknüpfte Liste ist. Weiß nicht, warum sie das über ein Array gewählt haben. –

+0

Viele Sprachen verwenden eine Linked-List-Struktur für ihre "dynamischen Arrays". Viele verwenden auch tatsächliche native Arrays, die mit Pufferarrays deklariert sind. Sobald die Puffer gefüllt sind, verketten sie entweder ein neues Array (erstellen eine Art Array/Hybridisierung verknüpfter Listen) oder weisen ein brandneues Array mit der richtigen neuen Puffergröße zu, kopieren dann alle vorhandenen Daten und zerstören das alte Array. Dies kann jedoch zu einer gewissen Speicherfragmentierung führen. –

+0

Ich habe mit UniData (UniBasic), "C" und Java-Systemen seit etwa 1993 gearbeitet ... die meisten Variablen in UDT sind nur Strings, im strengen "C" Sinne (wie UDT ist in "C" gebaut), die sind Arrays von Bytes. Daher gilt der Begriff "dynamisches Array", da sie veränderbar sind. Das Hinzufügen zum Anfang oder Ende ist in der Regel schneller, da es Zeiger darauf gibt, wo das ist. Das Hinzufügen zu der Mitte umfasst mehrere Schritte einschließlich mehrerer Speicherzuweisungen und Datenverschiebungen. Nicht effizient, genau wie Java String "+" (weshalb StringBuilder erfunden wurde). Versuchen Sie, DIMensioned Arrays oder CALLC zu verwenden. –

10

Um am Ende eines Arrays einzufügen, müssen Sie das Element einfach dort platzieren.

Um in die Mitte eines Arrays einzufügen, müssen Sie die Elemente nach diesem Punkt um eins nach oben verschieben.

Um am Ende eines Arrays zu löschen, löschen Sie einfach seine Zählung um eins.

Um aus der Mitte zu löschen, müssen Sie und die anderen Elemente nach unten verschieben.

Es ist die Verschiebung, die es in O (n) verwandelt.

1

Es ist ziemlich einfach:

in der Mitte einsetzen beinhaltet jedes später Element bewegt über von 1. am Ende einzufügen, wenn es zusätzlichen Platz reserviert ist, wird das Element nur dort gespeichert, aber wenn nicht neu Platz ist zugewiesen. Somit wird diese Operation in durchgeführt.

-2

Eigentlich ist es nicht Big-O, sondern Big-Theta.

+0

Nein, es ist Big-O. Das Einsetzen in eine Position kann dauernd dauern, wenn sich die Position am Ende befindet. Es ist nicht unten durch n begrenzt. –

0

Zu Adam Robinsons ausgezeichneter Zusammenfassung hinzufügen: Dies ist nicht nur Theorie. Ich habe eine beliebige Anzahl von Situationen gesehen, in denen ein dynamisches Array erstellt wurde, indem wiederholt an das Ende des Arrays angehängt wurde. Dies funktioniert auf Grund der Tatsache, dass das Array wiederholt neu zugewiesen werden muss, wobei jedes seiner Mitglieder gezwungen wird, in die neue Array-Struktur kopiert zu werden. Die Neuzuweisungen können nur bei 1/10 der Append-Operationen passieren, aber das ist schlimm genug und ist immer noch leistungsmäßig.

In STL kann diese Leistungseinbuße vermieden werden, indem vector::reserve(N) vor dem Schreiben in den Vektor aufgerufen wird; aber dieser Schritt wird oft übersehen.

+0

Ich vermute, dass es deshalb typisch ist, die Größe des Arrays bei jeder Speicherzuweisung zu verdoppeln. –

+0

Es ist eine typische Praxis für Anwendungsentwickler. Aber nehmen Sie nicht an, * dass die Klassenbibliothek dies tun wird; viele gewöhnliche wachsen stattdessen in konstanten Schritten. 10 ist die Nummer, an die ich mich von MFC erinnere. –

Verwandte Themen