it-swarm.com.de

Warum verwenden wir Arrays anstelle von anderen Datenstrukturen?

Bei der Programmierung habe ich keine Instanz gesehen, in der ein Array besser zum Speichern von Informationen geeignet ist als eine andere Form davon. Ich hatte tatsächlich gedacht, dass die hinzugefügten "Features" in Programmiersprachen dies verbessert hatten und sie dadurch ersetzten. Ich sehe jetzt, dass sie nicht ersetzt werden, sondern sozusagen ein neues Leben erhalten.

Also, was ist der Sinn von Arrays? 

Dies ist nicht der Grund, warum wir Arrays aus Sicht des Computers verwenden, sondern warum Arrays aus Programmiersicht (ein geringfügiger Unterschied). Was der Computer mit dem Array macht, war nicht der Punkt der Frage.

189
Xesaniel

Zeit für eine Unterrichtsstunde. Während wir heute in unseren ausgefallenen verwalteten Sprachen nicht viel über diese Dinge nachdenken, bauen sie auf demselben Fundament auf. Schauen wir uns also an, wie das Gedächtnis in C verwaltet wird.

Bevor ich eintauche, eine kurze Erklärung, was der Begriff "Zeiger" bedeutet. Ein Zeiger ist einfach eine Variable, die auf eine Stelle im Speicher "zeigt". Es enthält nicht den tatsächlichen Wert in diesem Speicherbereich, es enthält die Speicheradresse dazu. Stellen Sie sich einen Speicherblock als Mailbox vor. Der Zeiger wäre die Adresse für dieses Postfach.

In C ist ein Array einfach ein Zeiger mit einem Versatz, der Versatz gibt an, wie weit sich der Speicher im Speicher befinden soll. Dies stellt O(1) Zugriffszeit bereit. 

  MyArray   [5]
     ^       ^
  Pointer  Offset

Alle anderen Datenstrukturen bauen entweder darauf auf oder verwenden keinen angrenzenden Speicher für die Speicherung, was zu einer schlechten Zugriffszeit beim wahlfreien Zugriff führt (obwohl es andere Vorteile gibt, wenn kein sequenzieller Speicher verwendet wird).

Nehmen wir zum Beispiel an, wir haben ein Array mit 6 Zahlen (6,4,2,3,1,5) darin. Im Speicher würde es so aussehen:

=====================================
|  6  |  4  |  2  |  3  |  1  |  5  |
=====================================

In einem Array wissen wir, dass sich jedes Element im Speicher nebeneinander befindet. Ein C-Array (hier MyArray genannt) ist einfach ein Zeiger auf das erste Element:

=====================================
|  6  |  4  |  2  |  3  |  1  |  5  |
=====================================
   ^
MyArray

Wenn wir nach MyArray [4] suchen wollten, würde intern darauf zugegriffen werden:

   0     1     2     3     4 
=====================================
|  6  |  4  |  2  |  3  |  1  |  5  |
=====================================
                           ^
MyArray + 4 ---------------/
(Pointer + Offset)

Da wir direkt auf jedes Element im Array zugreifen können, indem Sie den Versatz zum Zeiger hinzufügen, können wir jedes Element in der gleichen Zeit nachschlagen, unabhängig von der Größe des Arrays. Das bedeutet, dass das Abrufen von MyArray [1000] dieselbe Zeit in Anspruch nehmen würde wie das Abrufen von MyArray [5].

Eine alternative Datenstruktur ist eine verknüpfte Liste. Dies ist eine lineare Liste von Zeigern, die jeweils auf den nächsten Knoten zeigen

========    ========    ========    ========    ========
| Data |    | Data |    | Data |    | Data |    | Data |
|      | -> |      | -> |      | -> |      | -> |      | 
|  P1  |    |  P2  |    |  P3  |    |  P4  |    |  P5  |        
========    ========    ========    ========    ========

P(X) stands for Pointer to next node.

Beachten Sie, dass ich jeden "Knoten" zu einem eigenen Block gemacht habe. Dies liegt daran, dass nicht garantiert ist, dass sie im Speicher nebeneinander liegen (und höchstwahrscheinlich auch nicht).

Wenn ich auf P3 zugreifen möchte, kann ich nicht direkt darauf zugreifen, weil ich nicht weiß, wo es sich im Speicher befindet. Ich weiß nur, wo sich die Wurzel (P1) befindet, ich muss stattdessen bei P1 beginnen und jedem Zeiger auf den gewünschten Knoten folgen. 

Dies ist eine O(N) -Zugriffszeit (die Nachschlagkosten steigen, wenn jedes Element hinzugefügt wird). Es ist viel teurer, zu P1000 zu gelangen, als zu P4.

Datenstrukturen auf höherer Ebene, wie Hashtabellen, Stacks und Warteschlangen, können alle intern ein Array (oder mehrere Arrays) verwenden, während Linked Lists und Binary Trees normalerweise Knoten und Zeiger verwenden.

Sie fragen sich vielleicht, warum jemand eine Datenstruktur verwenden würde, für die ein linearer Durchlauf erforderlich ist, um einen Wert nachzuschlagen, anstatt nur ein Array zu verwenden.

Nehmen Sie unser Array wieder. Dieses Mal möchte ich das Array-Element finden, das den Wert '5' enthält.

=====================================
|  6  |  4  |  2  |  3  |  1  |  5  |
=====================================
   ^     ^     ^     ^     ^   FOUND!

In dieser Situation weiß ich nicht, welchen Versatz ich dem Zeiger hinzufügen muss, um ihn zu finden, also muss ich bei 0 beginnen und mich nach oben bis zum Ziel vorarbeiten. Das heißt, ich muss 6 Prüfungen durchführen.

Aus diesem Grund wird die Suche nach einem Wert in einem Array als O (N) betrachtet. Die Suchkosten steigen mit zunehmendem Array. 

Erinnern Sie sich oben, wo ich sagte, dass die Verwendung einer nicht sequentiellen Datenstruktur manchmal Vorteile haben kann? Die Suche nach Daten ist einer dieser Vorteile und eines der besten Beispiele ist der Binary Tree.

Eine binäre Struktur ist eine Datenstruktur, die einer verknüpften Liste ähnelt. Anstatt jedoch einen einzelnen Knoten zu verknüpfen, kann jeder Knoten eine Verbindung zu zwei untergeordneten Knoten herstellen.

         ==========
         |  Root  |         
         ==========
        /          \ 
  =========       =========
  | Child |       | Child |
  =========       =========
                  /       \
            =========    =========
            | Child |    | Child |
            =========    =========

 Assume that each connector is really a Pointer

Wenn Daten in einen Binärbaum eingefügt werden, verwendet er mehrere Regeln, um zu entscheiden, wo der neue Knoten platziert werden soll. Das Grundkonzept besteht darin, dass der neue Wert, wenn er größer ist als der übergeordnete Wert, nach links eingefügt wird. Wenn er niedriger ist, wird er nach rechts eingefügt.

Dies bedeutet, dass die Werte in einem Binärbaum folgendermaßen aussehen könnten:

         ==========
         |   100  |         
         ==========
        /          \ 
  =========       =========
  |  200  |       |   50  |
  =========       =========
                  /       \
            =========    =========
            |   75  |    |   25  |
            =========    =========

Wenn Sie einen binären Baum nach dem Wert von 75 durchsuchen, müssen wir aufgrund dieser Struktur nur 3 Knoten (O (log N)) besuchen:

  • Ist 75 weniger als 100? Schauen Sie sich den rechten Knoten an
  • Ist 75 größer als 50? Schauen Sie sich den Linken Knoten an
  • Es gibt die 75!

Obwohl es 5 Knoten in unserem Baum gibt, mussten wir uns die verbleibenden zwei nicht ansehen, da wir wussten, dass sie (und ihre Kinder) möglicherweise nicht den gewünschten Wert enthalten könnten. Dies gibt uns eine Suchzeit, die im schlimmsten Fall bedeutet, dass wir jeden Knoten besuchen müssen, aber im besten Fall müssen wir nur einen kleinen Teil der Knoten besuchen.

Hier werden Arrays geschlagen, sie bieten eine lineare O(N) Suchzeit trotz O(1) Zugriffszeit.

Dies ist ein unglaublich hoher Überblick über Datenstrukturen im Speicher, wobei viele Details übersprungen werden, aber hoffentlich veranschaulicht es die Stärke und Schwäche eines Arrays im Vergleich zu anderen Datenstrukturen.

756
FlySwat

Für O(1) Direktzugriff, der nicht unterbunden werden kann.

72
jason

Nicht alle Programme machen dasselbe oder laufen auf derselben Hardware.

Dies ist normalerweise die Antwort, warum verschiedene Sprachfunktionen vorhanden sind. Arrays sind ein Kernkonzept der Informatik. Das Ersetzen von Arrays durch Listen/Matrizen/Vektoren/welche fortschrittliche Datenstruktur würde die Leistung stark beeinträchtigen und in einer Reihe von Systemen völlig unpraktisch sein. Es gibt eine Reihe von Fällen, in denen die Verwendung eines dieser "erweiterten" Datenerfassungsobjekte aufgrund des fraglichen Programms verwendet werden sollte. 

In der Geschäftsprogrammierung (was die meisten von uns tun) können wir auf relativ leistungsfähige Hardware abzielen. Die Verwendung einer Liste in C # oder Vector in Java ist in diesen Situationen die richtige Wahl, da diese Strukturen es dem Entwickler ermöglichen, die Ziele schneller zu erreichen, was wiederum dazu führt, dass diese Art von Software mit mehr Funktionen ausgestattet wird.

Beim Schreiben von Embedded-Software oder eines Betriebssystems ist ein Array oft die bessere Wahl. Ein Array bietet zwar weniger Funktionalität, beansprucht aber weniger RAM und der Compiler kann den Code für Lookups in Arrays effizienter optimieren.

Ich bin sicher, dass ich eine Reihe von Vorteilen für diese Fälle auslasse, aber ich hoffe, dass Sie den Punkt verstehen.

20
Jason Jackson

Um die Vorteile von Arrays zu sehen, können Sie herausfinden, wo die O(1) - Zugriffsmöglichkeit von Arrays erforderlich ist und daher aktiviert wird:

  1. In Nachschlagetabellen Ihrer Anwendung (ein statisches Array für den Zugriff auf bestimmte kategoriale Antworten)

  2. Memoization (bereits berechnete komplexe Funktionsergebnisse, damit Sie den Funktionswert nicht erneut berechnen, z. B. log x)

  3. High-Speed-Bildverarbeitungsanwendungen, die Bildverarbeitung erfordern ( https://de.wikipedia.org/wiki/Lookup_table#Lookup_tables_in_image_processing )

0
priya khokher