Ein Array oder Vektor ist nur eine Folge von Werten. Sie können sicher mit einer verknüpften Liste implementiert werden. Dies ist nur ein Bündel von Knoten mit Zeigern auf den nächsten Knoten.
Stapel und Warteschlangen sind zwei abstrakte Datentypen, die in Intro CS-Kursen gelehrt werden. Irgendwo in der Klasse müssen die Schüler häufig Stapel und Warteschlangen implementieren, indem sie eine verknüpfte Liste als zugrunde liegende Datenstruktur verwenden. Dies bedeutet, dass wir wieder zur gleichen Idee der "Sammlung von Knoten" zurückkehren.
Prioritätswarteschlangen können mit einem Heap erstellt werden. Ein Haufen kann als Baum mit dem minimalen Wert an der Wurzel betrachtet werden. Bäume aller Art, einschließlich BSTs, AVLs und Heaps, können als Sammlung von Knoten betrachtet werden, die durch Kanten verbunden sind. Diese Knoten sind dort miteinander verbunden, wo ein Knoten auf einen anderen zeigt.
Es scheint, als könne sich jedes Datenkonzept immer auf Knoten mit Zeigern auf einen anderen geeigneten Knoten beschränken. Ist das richtig? Wenn es so einfach ist, warum erklären Lehrbücher nicht, dass Daten nur ein Bündel von Knoten mit Zeigern sind? Wie gelangen wir von Knoten zu Binärcode?
quelle
Antworten:
Nun, das ist im Grunde genommen, worauf alle Datenstrukturen hinauslaufen. Daten mit Verbindungen. Die Knoten sind alle künstlich - sie existieren tatsächlich nicht physisch. Hier kommt der Binärteil ins Spiel. Sie sollten einige Datenstrukturen in C ++ erstellen und überprüfen, wo Ihre Objekte im Speicher landen. Es kann sehr interessant sein, zu erfahren, wie die Daten im Speicher abgelegt sind.
Der Hauptgrund für so viele verschiedene Strukturen ist, dass sie sich alle auf die eine oder andere Sache spezialisieren. Beispielsweise ist es in der Regel schneller, einen Vektor anstelle einer verknüpften Liste zu durchlaufen, da die Seiten aus dem Speicher abgerufen werden. Eine verknüpfte Liste ist besser zum Speichern größerer Typen geeignet, da Vektoren zusätzlichen Platz für nicht verwendete Slots reservieren müssen (dies ist beim Entwurf eines Vektors erforderlich).
Als Randnotiz ist eine interessante Datenstruktur, die Sie sich ansehen möchten, eine Hash-Tabelle. Es folgt nicht ganz dem von Ihnen beschriebenen Knoten- und Zeigersystem.
TL; DR: Container sind im Grunde alle Knoten und Zeiger, haben aber sehr spezifische Verwendungen und sind für etwas besser und für andere schlechter.
quelle
Oh, mein Lieber nein. Du tust mir weh.
Wie ich an anderer Stelle versucht habe zu erklären (" Was ist der Unterschied zwischen einem binären Suchbaum und einem binären Heap? "), Gibt es auch für eine feste Datenstruktur mehrere Ebenen, um dies zu verstehen.
Wie die Prioritätswarteschlange, die Sie erwähnen, ist sie ein abstrakter Datentyp, wenn Sie sie nur verwenden möchten. Sie verwenden es, um zu wissen, welche Art von Objekten darin gespeichert sind und welche Fragen Sie ihm stellen können. Das sind mehr Datenstrukturen als eine Tüte mit Gegenständen. Auf der nächsten Ebene der berühmten Implementierung, die binäre Haufen, kann verstanden als binärer Baum, aber die letzte Ebene ist aus Effizienzgründen als Array implementiert. Keine Knoten und Zeiger dort.
Und auch für Diagramme, die mit Sicherheit wie Knoten und Zeiger (Kanten) aussehen, stehen zwei grundlegende Darstellungen zur Verfügung: das Adjazenzarray und die Adjazenzlisten. Nicht alle Hinweise stelle ich mir vor.
Wenn Sie wirklich versuchen, Datenstrukturen zu verstehen, müssen Sie ihre guten Punkte und Schwächen untersuchen. Manchmal verwendet eine Darstellung ein Array aus Gründen der Effizienz (entweder räumlich oder zeitlich), manchmal gibt es Hinweise (aus Gründen der Flexibilität). Dies gilt auch, wenn Sie gute "vorgefertigte" Implementierungen wie die C ++ - STL haben, da Sie auch dort manchmal die zugrunde liegenden Darstellungen auswählen können. Da gibt es immer einen Kompromiss.
quelle
Machen wir eine Analogie zur Mathematik. Betrachten Sie den folgenden Satz: " ist eine stetige Funktion". Funktionen sind wirklich definiert als Beziehungen, die definiert sind als Mengen. Die Menge der reellen Zahlen ist das eindeutige vollständige, vollständig geordnete Feld: Alle diese Konzepte sind einfacher definiert. Um von Kontinuität zu sprechen, braucht man das Konzept der Nachbarschaft, das in Bezug auf eine Topologie definiert ist ... und so weiter bis zu den Axiomen von ZFC.f:R→R
Niemand erwartet von Ihnen, dass Sie all das sagen, um eine kontinuierliche Funktion zu definieren, sonst wäre niemand in der Lage, irgendwelche Arbeiten zu erledigen. Wir "vertrauen" einfach darauf, dass jemand die langweilige Arbeit für uns gemacht hat.
Jede Datenstruktur, die Sie sich vorstellen können, beschränkt sich auf die grundlegenden Objekte, mit denen Ihr zugrunde liegendes Rechenmodell umgeht, ganze Zahlen in einem Register, wenn Sie eine Maschine mit wahlfreiem Zugriff verwenden, oder Symbole auf einem Band, wenn Sie eine Turing-Maschine verwenden.
Wir verwenden Abstraktionen, weil sie unseren Geist von Trivialitäten befreien und es uns ermöglichen, über komplexere Probleme zu sprechen. Es ist durchaus vernünftig, nur zu "vertrauen", dass diese Strukturen funktionieren: Bis ins kleinste Detail zu gehen, ist - es sei denn, Sie haben einen bestimmten Grund dafür - eine vergebliche Übung, die Ihrem Argument nichts hinzufügt.
quelle
Hier ein Gegenbeispiel: In der λ-Rechnung läuft jeder Datentyp auf Funktionen hinaus. λ-Kalkül hat keine Knoten oder Zeiger, das einzige, was es hat, sind Funktionen, daher muss alles mit Funktionen implementiert werden.
Dies ist ein Beispiel für die Codierung von Booleschen Werten als Funktionen in ECMAScript:
Und das ist eine Nachteile-Liste:
Natürliche Zahlen können als Iteratorfunktionen implementiert werden.
Eine Menge ist dasselbe wie ihre charakteristische Funktion (dh die
contains
Methode).Beachten Sie, dass bei der obigen Kodierung von Booleschen Werten in der Kirche tatsächlich Bedingungen in OO-Sprachen wie Smalltalk implementiert werden, die keine Booleschen Werte, Bedingungen oder Schleifen als Sprachkonstrukte haben und diese lediglich als Bibliotheksfunktion implementieren. Ein Beispiel in Scala:
quelle
Viele (die meisten?) Datenstrukturen bestehen aus Knoten und Zeigern. Arrays sind ein weiteres kritisches Element einiger Datenstrukturen.
Letztendlich ist jede Datenstruktur nur eine Ansammlung von Wörtern im Speicher oder nur eine Ansammlung von Bits. Es ist wichtig, wie sie strukturiert sind und wie wir sie interpretieren und verwenden.
quelle
Die Implementierung von Datenstrukturen läuft immer auf Knoten und Zeiger hinaus, ja.
Aber warum dort aufhören? Die Implementierung von Knoten und Zeigern beschränkt sich auf Bits.
Die Implementierung von Bits beruht auf elektrischen Signalen, Magnetspeichern, möglicherweise Glasfaserkabeln usw. (kurz gesagt: Physik).
Dies ist die reductio ad absurdum der Aussage "Alle Datenstrukturen laufen auf Knoten und Zeiger hinaus." Es ist wahr - aber es bezieht sich nur auf die Implementierung.
Chris Date kann sehr gut zwischen Implementierung und Modell unterscheiden , obwohl sein Aufsatz sich insbesondere an Datenbanken richtet.
Wir können noch ein bisschen weiter gehen, wenn wir feststellen, dass es keine einzige Trennlinie zwischen Modell und Implementierung gibt. Dies ist ähnlich (wenn nicht identisch) mit dem Konzept der "Abstraktionsschichten".
Auf einer bestimmten Abstraktionsebene sind die Ebenen "unter" Ihnen (die Ebenen, auf denen Sie aufbauen) lediglich "Implementierungsdetails" für die Abstraktion oder das Modell, auf die Sie sich beziehen.
Die unteren Abstraktionsschichten selbst weisen jedoch Implementierungsdetails auf.
Wenn Sie ein Handbuch für eine Software lesen, lernen Sie die Abstraktionsschicht kennen, die von dieser Software "dargestellt" wird, auf der Sie Ihre eigenen Abstraktionen erstellen können (oder einfach Aktionen wie das Senden von Nachrichten ausführen können).
Wenn Sie die Implementierungsdetails der Software kennen, erfahren Sie, wie die Schöpfer die von ihnen erstellten Abstraktionen untermauerten. Die "Implementierungsdetails" können unter anderem Datenstrukturen und Algorithmen umfassen.
Sie würden die Spannungsmessung jedoch nicht als Teil der "Implementierungsdetails" für ein bestimmtes Softwareteil betrachten, obwohl dies dahingehend erklärt wird, wie "Bits" und "Bytes" und "Speicher" tatsächlich auf dem physischen Computer funktionieren.
Zusammenfassend sind Datenstrukturen eine Abstraktionsschicht zum Überlegen und Implementieren von Algorithmen und Software. Die Tatsache, dass diese Abstraktionsschicht auf Implementierungsdetails niedrigerer Ebene wie Knoten und Zeiger aufbaut, ist wahr, aber innerhalb der Abstraktionsschicht irrelevant .
Ein großer Teil des Verständnisses eines Systems ist das Erfassen, wie die Abstraktionsschichten zusammenpassen. Daher ist es wichtig zu verstehen, wie Datenstrukturen implementiert werden. Aber die Tatsache , dass sie sind , umgesetzt werden , bedeutet nicht , dass die Abstraktion von Datenstrukturen nicht vorhanden ist .
quelle
Ein Array oder ein Vektor kann mit einer verknüpften Liste implementiert werden, sollte dies aber so gut wie nie sein.
Wenn Sie Ihren Anwendungsbereich ein wenig erweitern, um physisch zusammenhängende Arrays in Ihre Toolbox aufzunehmen, können tatsächlich fast alle praktischen Datenstrukturen mit diesen zusammen mit Knoten und Zeigern implementiert werden.
quelle
Denn das ist nicht was "Daten" bedeutet. Sie verbinden abstrakte Ideen mit Umsetzungen. "Daten" ist eine sehr abstrakte Idee: Es ist nur ein anderer Name für "Informationen". Ein Bündel verknüpfter Knoten mit Zeigern (auch als "verknüpfte Datenstruktur" bezeichnet) ist eine viel konkretere Idee: Es handelt sich um eine bestimmte Art der Darstellung und Organisation von Informationen.
Einige Datenabstraktionen eignen sich sehr gut für "verknüpfte" Implementierungen. Es gibt nicht viele gute Möglichkeiten, die Verzweigung eines vollständig allgemeinen Baums ohne die Verwendung expliziter Knoten und Zeiger (oder einer gewissen Isomorphie von Knoten und Zeigern) zu implementieren. Andererseits gibt es andere Abstraktionen, die Sie niemals mit Knoten und Zeigern implementieren würden. Fließkommazahlen kommen in den Sinn.
Stapel und Warteschlangen liegen irgendwo dazwischen. Es gibt Zeiten, in denen eine verknüpfte Implementierung eines Stacks sehr sinnvoll ist. In anderen Fällen ist es viel sinnvoller, ein Array und einen einzelnen "Stapelzeiger" zu verwenden.
quelle