Verwenden Sie Python, um grundlegende Datenstrukturen zu implementieren [01/4]

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

  1. Erfüllt die Datenstruktur die von der ADT-Domäne angegebenen Speicheranforderungen?
  2. Bieten die Datenstrukturen den Datenzugriff und die Datenbearbeitungsmöglichkeiten, um ADT vollständig zu implementieren?
  3. 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.

Supongo que te gusta

Origin blog.csdn.net/gongdiwudu/article/details/132787294
Recomendado
Clasificación