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:
Klasse Bag: „““ Konstruktor: Größe enthält Append Remove 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): Artikel in self._items bestätigen, „Artikel muss in der Tasche“ zurückgeben self._items .remove(item) def __iter__(self): return _BagIterator(self._items) class _BagIterator:_items) """ Beachten Sie, dass die Iterator-Klasse hier implementiert ist""" def __init__(self, seq): self._bag_items = seq self._cur_item = 0 def __iter__(self): return self def __next__(self): if self._cur_item < len(self._bag_items): item = self._bag_items[ self._cur_item] self._cur_item += 1 Rückgabeelement sonst: raise StopIteration b = Bag() b.add(1) b.add(2) for i in b: # for使用__iter__构建,用__next__迭代 print(i) """ # for 语句等价于 i = b.__iter__() while True: Versuchen Sie: item = i.__next__() print(item) außer StopIteration: break „““
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-Klasse importieren Array: def __init__(self, size): Assert size > 0, 'array size must be > 0' self._size = size PyArrayType = ctypes.py_object * size self._elements = PyArrayType() self.clear(None ) 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 , Wert): Behauptung Index >= 0 und Index < len(self), 'out of range' self._elements[index] = value def clear(self, value): „““ Setzen Sie jedes Element auf den Wert „““ for i in range(len(self)): self._elements[i] = value def __iter__(self): return _ArrayIterator(self._elements) class _ArrayIterator: def __init__(self, items): self._items = items self. _idx = 0 def __iter__(self): return self def __next__(self): if self._idex < len(self._items): val = self._items[self._idx] self._idex += 1 return val else: raise StopIteration
2.1 Zweidimensionale Arrays
class Array2D: „““ Neues Array2D(nrows, ncols): Konstruktor numRows() numCols() clear(value) getitem(i, j) setitem(i, j, val) „““ def __init__(self, numrows, numcols): self._the_rows = Array(numrows) # Beispiel für i in range(numrows): self._the_rows[i] = Array(numcols) @property def numRows(self): return len(self._the_rows ) @property def NumCols(self): return len(self._the_rows[0]) def clear(self, value): für Zeile in self._the_rows: row.clear(value) def __getitem__(self, ndx_tuple): # ndx_tuple: (x, y) Assert len(ndx_tuple) == 2 row, col = ndx_tuple[0], ndx_tuple[1] Assert (row >= 0 and row < self.numRows and col >= 0 and col < self.NumCols) the_1d_array = self._the_rows[row] return the_1d_array[col] def __setitem__(self, ndx_tuple, value): Assert len(ndx_tuple) == 2 row, col = ndx_tuple[0], ndx_tuple[1] affirm (row >= 0 and row < self.numRows and col >= 0 and col < self.NumCols) the_1d_array = self._the_rows[row] the_1d_array[col] = value
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: „““ 最好用pandas的DataFrame Matrix(rows, ncols): Konstruktor numCols() getitem(row, col) setitem(row, col, val) scaleBy(scalar): 每个元素乘scalar transpose() : Transpose hinzufügen add(rhsMatrix): size must be the same subtract(rhsMatrix) multiply(rhsMatrix) """ def __init__(self, numRows, numCols): self._theGrid = Array2D(numRows, numCols) self._theGrid. clear(0) @property def numRows(self): return self._theGrid.numRows @property def NumCols(self): return self._theGrid.numCols def __getitem__(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] *= scalar def __add__(self, rhsMatrix): Assert (rhsMatrix.numRows = = self.numRows und rhsMatrix.numCols == self.numCols) newMartrix = Matrix(self.numRows, self.numCols) für r im Bereich(self.numRows): für c im Bereich(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.
class Set: „““ 使用list实现set ADT Set() length() enthält(Element) add(Element) Remove(Element) equal(Element) isSubsetOf(setB) Union(setB) intersect(setB) Differenz(setB) Iterator () """ def __init__(self): self._theElements = list() def __len__(self): return len(self._theElements) def __contains__(self, element): return element in self._theElements def add(self, element): wenn Element nicht in self: self._theElements.append(element) def remove(self, element): Element in self bestätigen, „Das Element muss gesetzt werden“ self._theElements.remove(element) def __eq__(self, setB): if len(self) != len(setB): false zurückgeben sonst: return self.isSubsetOf(setB) def isSubsetOf(self, setB): für Element in self: wenn Element nicht in setB: return False return True def union(self, setB): newSet = Set() newSet._theElements.extend (self._theElements) für Element in setB: wenn Element nicht in self: newSet._theElements.append(element) gibt newSet zurück
3.2 Maps oder Dict: Schlüssel-Wert-Paare, intern von Python mithilfe von Hash implementiert.
Klasse Map: „““ Map ADT-Listenimplementierung Map() length() contains(key) add(key, value) remove(key) 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 is not None def add(self, key, value): ndx = self ._findPosition(key) wenn ndx nicht None ist: self._entryList[ndx].value = value return False sonst: Eintrag = _MapEntry(Schlüssel, Wert) self._entryList.append(Eintrag) gibt True zurück def valueOf(selbst, Schlüssel): ndx = self._findPosition(Schlüssel) behaupten, ndx ist nicht None, „Ungültiger Kartenschlüssel“ gibt self zurück. _entryList[ndx].value def remove(self, key): ndx = self._findPosition(key) affirm ndx is not None, 'Invalid map key' self._entryList.pop(ndx) def __iter__(self): return _MapIterator( self._entryList) def _findPosition(self, key): for i in range(len(self)): if self._entryList[i].key == key: return i return None class _MapEntry: # oder usecollections.namedtuple('_MapEntry', 'key,value') def __init__(self, key, value): self.key = key self.value = value
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-Haupt- oder Spalten-Hauptreihenfolge, dies ist Zeilen-Hauptreihenfolge MultiArray(d1, d2, ...dn) dims(): die Anzahl der Dimensionen length(dim): die Länge des gegebenen Arrays Dimension klar(Wert) getitem(i1, i2, ... in), Index(i1,i2,i3) = i1*(d2*d3) + i2*d3 + i3 setitem(i1, i2, ... in) Beschreibung: 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, Sie können dies auch tun.。assessieren Sie len(dimensions) > 1, 'Das Array muss 2 oder mehr Dimensionen haben' self._dims = Maße # Berechnen Sie die Gesamtzahl der Elemente im Array size = 1 für d in den Dimensionen: Assert d > 0, 'Dimensions must be > 0' size *= d # Erstellen Sie das 1-D-Array zum Speichern der Elemente self._elements = Array (size) # Erstellen Sie ein 1-D-Array zum Speichern der Gleichungsfaktoren self._factors = Array(len(dimensions)) self._computeFactors() @property def numDims(self): return len(self._dims) def length(self , dim): bestätige dim > 0 und dim < len(self._dims), „Dimensionskomponente außerhalb des Bereichs“ gibt self._dims[dim-1] zurück def clear(self, value): self._elements.clear(value) def __getitem__(self, ndxTuple): Assert len(ndxTuple) == self.numDims, 'Invalid # of array subscripts' index = self._computeIndex(ndxTuple) Der Assert-Index ist nicht None, „Array-Index außerhalb des Bereichs“. return self._elements[index] def __setitem__(self, ndxTuple, value): Assert len(ndxTuple) == self.numDims, „Ungültige Anzahl der Array-Subskripte“ index = self._computeIndex(ndxTuple) behauptet, Index sei nicht None, „Array-Index außerhalb des Bereichs“ self._elements[index] = value 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 None else: offset += ndexTuple[j] * self._factors[j] return offset
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.