veranschaulichen
Wenn Sie dieses Wissen nutzen müssen, es aber nicht haben, ist das frustrierend und kann zur Ablehnung des Vorstellungsgesprächs führen. Unabhängig davon, ob Sie ein paar Tage mit „Blitzen“ verbringen oder fragmentierte Zeit zum Weiterlernen nutzen, lohnt es sich, an der Datenstruktur zu arbeiten. Welche Datenstrukturen gibt es also in Python? Listen, Wörterbücher, Mengen und ... Stapel? Hat Python einen Stack? Diese Artikelserie enthält detaillierte Puzzleteile.
Kapitel 1: Abstrakter ADT-Datentyp, Definition von Daten und ihren Operationen
Was ist ADT: Abstrakter Datentyp? Jeder, der Datenstrukturen studiert hat, sollte es wissen.
So wählen Sie eine Datenstruktur für ADT aus
- Erfüllt die Datenstruktur die von der ADT-Domäne angegebenen Speicheranforderungen?
- Bieten die Datenstrukturen den Datenzugriff und die Datenbearbeitungsmöglichkeiten, um ADT vollständig zu implementieren?
- Effiziente Ausführung? Basierend auf Komplexitätsanalyse.
Der folgende Code ist ein einfaches Beispiel. Um beispielsweise eine einfache Bag-Klasse zu implementieren, definieren wir zunächst deren Operationen und verwenden dann die magischen Methoden der Klasse, um diese Methoden zu implementieren:
Klassentasche: „““ Konstruktor: Konstruktor Größe enthält anhängen entfernen iter „““ def __init__(self): self._items = list() def __len__(self): return len(self._items) def __contains__(self, item): Artikel in self._items zurückgeben def add(self, item): self._items.append(item) def remove(self, item): Behaupten Sie den Artikel in self._items, „Artikel muss in der Tasche“ return self._items.remove(item) def __iter__(self): return _BagIterator(self._items) Klasse _BagIterator: „““Beachten Sie, dass die Iterator-Klasse hier implementiert ist“““ def __init__(self, seq): self._bag_items = seq self._cur_item = 0 def __iter__(self): selbst zurückkehren def __next__(self): wenn self._cur_item < len(self._bag_items): item = self._bag_items[self._cur_item] self._cur_item += 1 Artikel zurückgeben anders: Erhöhen Sie StopIteration b = Tasche() b.add(1) b.add(2) for i in b: # for wird mit __iter__ erstellt und mit __next__ iteriert drucken(i) „““ # Die for-Anweisung ist äquivalent zu i = b.__iter__() während True: versuchen: item = i.__next__() drucken(Artikel) außer StopIteration: brechen „““
Kapitel 2: Array und Liste
Array: feste Länge, begrenzte Operationen, spart aber Speicher; es scheint, dass ich es in meiner Karriere noch nie verwendet habe, aber ich habe es in Python3.5 ausprobiert und es verfügt über eine Array-Klasse, die direkt mit „Import Array“ importiert werden kann
list: reserviert im Voraus Speicher und verfügt über umfangreiche Vorgänge, verbraucht jedoch Speicher. Ich habe Experimente mit sys.getsizeof durchgeführt. Mein persönliches Verständnis ist, dass es dem Vektor in C++ STL sehr ähnlich ist, der am häufigsten verwendeten Datenstruktur.
- list.append: Wenn zuvor nicht genügend Speicher zugewiesen wurde, wird ein neuer Bereich erneut geöffnet, und dann werden die vorherigen Daten kopiert und die Komplexität verringert.
- list.insert: verschiebt alle Elemente nach dem eingefügten Bereich, O(n)
- list.pop: Unterschiedliche Positionen von Pop erfordern unterschiedliche Komplexität. Pop (0) hat eine Komplexität von O (1) und die erste Position von Pop () hat eine Komplexität von O (n).
- list[]: Slice-Operation kopiert Daten (Speicherplatz reservieren) in eine andere Liste
So implementieren Sie ein ADT-Array:
ctypes importieren Klassenarray: def __init__(self, size): Behauptungsgröße > 0, „Arraygröße muss > 0 sein“ self._size = Größe PyArrayType = ctypes.py_object * Größe self._elements = PyArrayType() self.clear(Keine) def __len__(self): return self._size def __getitem__(self, index): bestätige Index >= 0 und Index < len(self), 'out of range' return self._elements[index] def __setitem__(self, index, value): bestätige Index >= 0 und Index < len(self), 'out of range' self._elements[index] = Wert def clear(self, value): „““ Setzen Sie jedes Element auf den Wert „““ für i in range(len(self)): self._elements[i] = Wert def __iter__(self): return _ArrayIterator(self._elements) Klasse _ArrayIterator: def __init__(self, items): self._items = Artikel self._idx = 0 def __iter__(self): selbst zurückkehren def __next__(self): wenn self._idex < len(self._items): val = self._items[self._idx] self._idex += 1 Rückgabewert anders: Erhöhen Sie StopIteration
2.1 Zweidimensionale Arrays
Klasse Array2D: „““ Die zu implementierende Methode Array2D(nrows, ncols): Konstruktor numRows() numCols() klar(Wert) getitem(i, j) setitem(i, j, val) „““ def __init__(self, numrows, numcols): self._the_rows = Array(numrows) # Array von Arrays für i im Bereich(numrows): self._the_rows[i] = Array(numcols) @Eigentum def numRows(self): return len(self._the_rows) @Eigentum def NumCols(self): return len(self._the_rows[0]) def clear(self, value): für Zeile in self._the_rows: row.clear(Wert) def __gettime__(self, ndx_tuple): #ndx_tuple: (x, y) behaupten len(ndx_tuple) == 2 Zeile, Spalte = ndx_tuple[0], ndx_tuple[1] behaupten (row >= 0 und row < self.numRows und col >= 0 und col < self.NumCols) the_1d_array = self._the_rows[row] gib the_1d_array[col] zurück def __setitem__(self, ndx_tuple, value): behaupten len(ndx_tuple) == 2 Zeile, Spalte = ndx_tuple[0], ndx_tuple[1] behaupten (row >= 0 und row < self.numRows und col >= 0 und col < self.NumCols) the_1d_array = self._the_rows[row] the_1d_array[col] = Wert
2.2 Die Matrix ADT, m Zeilen, n Spalten. Es ist am besten, Pandas zum Verarbeiten der Matrix zu verwenden. Es ist schmerzhafter, sie selbst zu implementieren.
Klassenmatrix: „““ Am besten verwenden Sie Pandas DataFrame Matrix(rows, ncols): Konstruktor numCols() getitem(row, col) setitem(row, col, val) ScaleBy(Skalar): Multiplizieren Sie jedes Element mit dem Skalar transpose(): gibt transpose transpose zurück add(rhsMatrix): Größe muss gleich sein subtrahieren(rhsMatrix) multiplizieren(rhsMatrix) „““ def __init__(self, numRows, numCols): self._theGrid = Array2D(numRows, numCols) self._theGrid.clear(0) @Eigentum def numRows(self): return self._theGrid.numRows @Eigentum def NumCols(self): return self._theGrid.numCols def __gettime__(self, ndxTuple): return self._theGrid[ndxTuple[0], ndxTuple[1]] def __setitem__(self, ndxTuple, scalar): self._theGrid[ndxTuple[0], ndxTuple[1]] = Skalar def scaleBy(self, scalar): für r in range(self.numRows): für c in range(self.numCols): self[r, c] *= Skalar def __add__(self, rhsMatrix): behaupten (rhsMatrix.numRows == self.numRows und rhsMatrix.numCols == self.numCols) newMartrix = Matrix(self.numRows, self.numCols) für r in range(self.numRows): für c in range(self.numCols): newMartrix[r, c] = self[r, c] + rhsMatrix[r, c]
Kapitel 3: Mengen und Karten
Neben der Liste sind wahrscheinlich die in Python integrierten Sets und Diktate die am häufigsten verwendeten.
3.1 legt ADT fest
Eine Sammlung ist ein Container, der eine Sammlung eindeutiger Werte in einer bestimmten vergleichbaren Domäne speichert, wobei die Werte in keiner bestimmten Reihenfolge gespeichert werden.
Klassensatz: „““ Verwenden Sie die Liste, um den Satz ADT zu implementieren Satz() Länge() enthält(Element) hinzufügen(Element) (Element) entfernen gleicht(Element) isSubsetOf(setB) Union(setB) schneiden(setB) Differenz(setB) Iterator() „““ def __init__(self): self._theElements = list() def __len__(self): return len(self._theElements) def __contains__(self, element): Rückgabeelement in self._theElements def add(self, element): wenn Element nicht in sich selbst: self._theElements.append(element) def remove(self, element): Element in sich selbst behaupten, „Das Element muss festgelegt werden“ self._theElements.remove(element) def __eq__(self, setB): wenn len(self) != len(setB): falsch zurückgeben anders: return self.isSubsetOf(setB) def isSubsetOf(self, setB): für Element in sich selbst: wenn Element nicht in setB: falsch zurückgeben Rückgabe True def Union(self, setB): newSet = Set() newSet._theElements.extend(self._theElements) für Element in setB: wenn Element nicht in sich selbst: newSet._theElements.append(element) newSet zurückgeben
3.2 Maps oder Dict: Schlüssel-Wert-Paare, intern von Python mithilfe von Hash implementiert.
Klassenkarte: „““ ADT-Listenimplementierung zuordnen Karte() Länge() enthält(Schlüssel) add(Schlüssel, Wert) (Schlüssel) entfernen valudOf(key) Iterator() „““ def __init__(self): self._entryList = list() def __len__(self): return len(self._entryList) def __contains__(self, key): ndx = self._findPosition(key) return ndx ist nicht None def add(self, key, value): ndx = self._findPosition(key) wenn ndx nicht None ist: self._entryList[ndx].value = Wert falsch zurückgeben anders: Eintrag = _MapEntry(Schlüssel, Wert) self._entryList.append(Eintrag) Rückgabe True def valueOf(self, key): ndx = self._findPosition(key) behaupten, ndx sei nicht None, „Ungültiger Kartenschlüssel“ return self._entryList[ndx].value def remove(self, key): ndx = self._findPosition(key) behaupten, ndx sei nicht None, „Ungültiger Kartenschlüssel“ self._entryList.pop(ndx) def __iter__(self): return _MapIterator(self._entryList) def _findPosition(self, key): für i in range(len(self)): if self._entryList[i].key == key: Rückkehr i return Keine class _MapEntry: # oder verwenden Siecollections.namedtuple('_MapEntry', 'key,value') def __init__(self, key, value): self.key = Schlüssel self.value = Wert
3.3 Das multiArray ADT, ein mehrdimensionales Array, wird im Allgemeinen unter Verwendung eines eindimensionalen Arrays simuliert, und dann werden die Elemente durch Berechnen des Indexes erhalten
Klasse MultiArray: „““ Zeilen- oder Spalten-Hauptreihenfolge, dies ist die Zeilen-Hauptreihenfolge MultiArray(d1, d2, ...dn) dims(): die Anzahl der Dimensionen length(dim): die Länge der angegebenen Array-Dimension klar(Wert) getitem(i1, i2, ... in), index(i1,i2,i3) = i1*(d2*d3) + i2*d3 + i3 setitem(i1, i2, ... in) Berechnen Sie den Index: index(i1,i2,...in) = i1*f1 + i2*f2 + ... + i(n-1)*f(n-1) + in*1 „““ def __init__(self, *dimensions): # Implementierung von MultiArray ADT unter Verwendung eines 1-D # Arrays, Arrays von Arrays von Arrays. . . behaupten len(dimensions) > 1, „Das Array muss 2 oder mehr Dimensionen haben“ self._dims = Abmessungen # Berechnen Sie die Gesamtzahl der Elemente im Array Größe = 1 für d in den Abmessungen: behaupten d > 0, 'Abmessungen müssen > 0 sein' Größe *= d # Erstellen Sie das 1D-Array zum Speichern der Elemente self._elements = Array(Größe) # Erstellen Sie ein 1D-Array zum Speichern der Gleichungsfaktoren self._factors = Array(len(dimensions)) self._computeFactors() @Eigentum def numDims(self): return len(self._dims) def length(self, dim): behaupten dim > 0 und dim < len(self._dims), 'Dimensionskomponente außerhalb des Bereichs' return self._dims[dim-1] def clear(self, value): self._elements.clear(Wert) def __gettime__(self, ndxTuple): behaupten len(ndxTuple) == self.numDims, 'Ungültige Anzahl von Array-Indizes' index = self._computeIndex(ndxTuple) Der Assertion-Index ist nicht None, „Array-Index außerhalb des gültigen Bereichs“ return self._elements[index] def __setitem__(self, ndxTuple, value): behaupten len(ndxTuple) == self.numDims, 'Ungültige Anzahl von Array-Indizes' index = self._computeIndex(ndxTuple) Der Assertion-Index ist nicht None, „Array-Index außerhalb des gültigen Bereichs“ self._elements[index] = Wert def _computeIndex(self, ndxTuple): # unter Verwendung der Gleichung: i1*f1 + i2*f2 + ... + in*fn Offset = 0 für j in range(len(ndxTuple)): wenn ndxTuple[j] < 0 oder ndxTuple[j] >= self._dims[j]: return Keine anders: offset += ndexTuple[j] * self._factors[j] Rückgabeoffset
Teil 4: Algorithmusanalyse
Die Big-O-Notation wird im Allgemeinen verwendet, um die durchschnittliche Zeitkomplexität des Algorithmus zu messen: 1 < log(n) < n < nlog(n) < n^2 < n^3 < a^n. Das Verständnis der durchschnittlichen zeitlichen Komplexität gängiger Datenstrukturoperationen ist hilfreich, um effizientere Datenstrukturen zu verwenden. Natürlich muss sie manchmal zeitlich und räumlich gemessen werden, und einige Operationen können sich sogar verschlechtern, wie z. B. die Anhängeoperation der Liste. Wenn Der Listenplatz reicht nicht aus, wird neuen Platz eröffnen, die Operationskomplexität verringert sich auf O(n) und manchmal ist es notwendig, eine amortisierte Analyse (amortisiert) zu verwenden.