Verwenden Sie Python, um grundlegende Datenstrukturen zu implementieren [04/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 13: Binärbaum

Der Binärbaum: Binärbaum, jeder Knoten hat nur zwei untergeordnete Knoten.

class _BinTreeNode: 
    def __init__(self, data): 
        self.data = data 
        self.left = None 
        self.right = None 


# 三种 Depth-First遍历
def preorderTrav(subtree): 
    """ 先(根)序遍历"" " 
    wenn der Teilbaum nicht None ist: 
        print(subtree.data) 
        preorderTrav(subtree.left) 
        preorderTrav(subtree.right) 


def inorderTrav(subtree): 
    """ 中(根)序遍历""" 
    wenn der Teilbaum nicht None ist: 
        preorderTrav (subtree.left) 
        print(subtree.data) 
        preorderTrav(subtree.right) 


def postorderTrav(subtree):
    „““ Post-(Root-)Order-Traversal“ 
    wenn der Teilbaum nicht „None“ ist:
        preorderTrav(subtree.left) 
        preorderTrav(subtree.right) 
        print(subtree.data) 


# 宽度优先遍历(bradth-First Traversal): 一层一层遍历, 使用queue 
def widththFirstTrav(bintree): 
    from queue import Queue # py3 
    q = Queue() 
    q.put(bintree) 
    while not q.empty(): 
        node = q.get() 
        print(node.data) 
        if node.left is not None: 
            q.put(node.left) 
        if node. right ist nicht None: 
            q.put(node.right) 


class _ExpTreeNode: 
    __slots__ = ('element', 'left', 'right') 

    def __init__(self, data): 
        self.element = data 
        self.left = None 
        self.right = None 

    def __repr__(self): 
        return '<_ExpTreeNode: {} {} {}>'.format( 
            self.element, self.left, self.right) 

from queue import Queue 
class ExpressionTree : 
    """ 
    Ausdrucksbaum: Ein binärer Baum, in dem Operatoren in inneren Knoten und Operanden in Blattknoten gespeichert werden. (Der Symbolbaum ist wirklich schwer zu tippen) * 
        / 
       \ 
      + - 
     / \ / \ 
     9 3 8 4 
    ( 9+3) * (8-4) Der 

    abstrakte Datentyp „Expression Tree“ kann binäre Operatoren implementieren. 
    ExpressionTree(expStr): Benutzerzeichenfolge als Konstruktorparam „ 
    evaluieren(varDict):“ wertet den Ausdruck aus und gibt das numerische Ergebnis zurück 
    erstellt eine Zeichenfolgendarstellung des Ausdrucks und gibt sie zurück. 

    Verwendung: 
        vars = {'a': 5, 'b':12} 
        expTree = ExpressionTree("(a/(b-3))") 
        print('The result = ', expTree.evaluate(vars))
    """ 

    def __init__(self, expStr): 
        self._expTree = Keine 
        self._buildTree(expStr) 

    defvalue(self, varDict): 
        return self._evalTree(self._expTree, varDict) 

    def __str__(self): 
        return self. _buildString(self._expTree) 

    def _buildString(self, treeNode): 
        „““ Fügen Sie Klammern hinzu, bevor ein Teilbaum durchlaufen wird, und fügen Sie schließende Klammern hinzu, nachdem der Teilbaum durchlaufen wird „““ 
        # print(treeNode) 
            return expStr 
        wenn treeNode.left None und treeNode.right None ist: 
            return str(treeNode.Element) #Der Blattknoten ist der Operand und gibt direkt zurück 
        :
            expStr = '(' 
            expStr += self._buildString(treeNode.left) 
            expStr += str(treeNode.element) 
            expStr += self._buildString(treeNode.right) 
            expStr += ')' 

    def _evalTree(self, subtree, varDict ): 
        # Ist es ein Blattknoten? Wenn ja, bedeutet es, dass es ein Operand ist und direkt zurückgegeben wird, 
        wenn subtree.left None und subtree.right None ist: 
            # Ist der Operand eine zulässige Zahl, 
            wenn subtree.element >= '0' und subtree.element <= '9': 
                return int(subtree.element) 
            else: # Der Operand ist eine Variable. 
                Assert subtree.element in varDict, 'ungültige Variable'. 
                return varDict[subtree.element] 
        else:Der #-Operator wertet seine Unterausdrücke aus
            lvalue = self._evalTree(subtree.left, varDict)  
            rvalue = self._evalTree(subtree.right, varDict) 
            print(subtree.element) 
            return self._computeOp(lvalue, subtree.element,rWert)

    def _computeOp(self, left, op, right): 
        Assert op 
        op_func = { 
            '+': Lambda left, right: left + right, # oder Importoperator, Operator.add 
            '-': Lambda links, rechts: links - rechts, 
            '*': Lambda links, rechts: links * rechts, 
            '/': Lambda links, rechts: links / rechts, 
            '%': Lambda links, rechts: links % right, 
        } 
        return op_func[op](left, right) 

    def _buildTree(self, expStr): 
        expQ = Queue() 
        for token in expStr: # 遍历表达式字符串的每个字符
            expQ.put(token)
        self._expTree = _ExpTreeNode(None) # 创建root节点
        self._recBuildTree(self._expTree, expQ) 

    def _recBuildTree(self, curNode, expQ): 
        token = expQ.get() 
        if token == '(': 
            curNode.left = _ExpTreeNode(None) 
            self._recBuildTree(curNode.left, expQ) 

            # nächstes Token wird ein Operator sein: + = * / % 
            curNode.element = expQ.get() 
            curNode.right = _ExpTreeNode(None) 
            self._recBuildTree(curNode .right, expQ) 

            # das nächste Token wird ')' sein, entferne es 
            expQ.get() 

        else: # das Token ist eine Ziffer, die in einen int umgewandelt werden muss. 
            curNode.element = token 


vars = {'a': 5, 'b': 12} 
expTree = ExpressionTree("((2*7)+8)") 
print(expTree) 
print('The result = ', expTree. bewerten(vars))

Heap: Eine der direktesten Anwendungen von Binärbäumen ist die Implementierung von Heaps. Der Heap ist ein vollständiger Binärbaum. Die Werte der Nicht-Blattknoten im größten Heap sind größer als die der untergeordneten Knoten, und die Werte der Nicht-Blattknoten im kleinsten Heap sind kleiner als die der untergeordneten Knoten. Python verfügt über ein integriertes Heapq-Modul, das uns bei der Implementierung von Heap-Operationen hilft, z. B. die Verwendung des integrierten Heapq-Moduls zur Implementierung der Heap-Sortierung:

# Verwenden Sie Pythons integriertes Heapq, um die Heap-Sortierung zu implementieren 
def heapsort(iterable): 
    from heapq import heappush, heappop 
    h = [] 
    for value in iterable: 
        heappush(h, value) 
    return [heappop(h) for i in range(len (h) ))]

Im Allgemeinen erfolgt die Implementierung eines Heaps jedoch nicht durch das Zählen von Knoten, sondern durch die Verwendung von Arrays, was effizienter ist. Warum kann es mit einem Array implementiert werden? Aufgrund der Natur eines vollständigen Binärbaums kann die Beziehung zwischen Indizes verwendet werden, um die Beziehung zwischen Knoten darzustellen. Dies wurde im Dokumentstring von MaxHeap erläutert

Klasse MaxHeap: 
    „““ 
    Heaps: 
    Vollständiger Binärbaum. Die Werte der Nicht-Blattknoten des maximalen Heaps sind größer als die untergeordneten Werte und die Werte der Nicht-Blattknoten des minimalen Heaps sind kleiner als Die Kinder. Der Heap 
    enthält zwei Eigenschaften, die Eigenschaft „Reihenfolge“ und die Eigenschaft „Form“ (ein vollständiger Binärbaum). Wenn Sie 
    einen neuen Knoten einfügen, behalten Sie immer diese beiden Attribute bei. 
    Einfügungsvorgang: Behalten Sie die Heap-Attribute und vollständigen Binärbaumattribute bei, die Sift-up-Operation behält bei Die Heap-Attribute. 
    Extraktionsvorgang: Nur die Wurzelknotendaten abrufen und den Baum konvertieren. Nachdem der Knoten ganz rechts unten auf den Wurzelknoten kopiert wurde, behält der Sift-Down-Vorgang das Heap-Attribut bei. Verwenden Sie ein Array, um den Heap zu implementieren. Beginnend 

    mit Nummerieren Sie jeden Knoten vom Wurzelknoten aus von oben nach unten und von links nach rechts. 
    Definieren Sie gemäß den Eigenschaften eines vollständigen Binärbaums einen Knoten i, und die Nummern seiner übergeordneten und untergeordneten Knoten sind: 
        parent = (i-1) / / 2 
        left = 2 * i + 1 
        rgiht = 2 * i + 2 
    Die Verwendung eines Arrays zur Implementierung eines Heaps ist effizienter und spart Geld. Durch die Speichernutzung von Baumknoten können auch komplexe Zeigeroperationen vermieden und 
    die Schwierigkeit des Debuggens verringert werden. 

    "" " 

    def __init__(self, maxSize): 
        self._elements = Array(maxSize) # Array ADT implementiert in Kapitel 2
        self._count = 0 

    def __len__(self): 
        return self._count 

    def Capacity(self): 
        return len(self._elements) 

    def add(self, value): 
        Assert self._count < self.capacity(), 'kann nicht Zum vollständigen Heap hinzufügen' 
        self._elements[self._count] = value 
        self._count += 1 
        self._siftUp(self._count - 1) 
        self.assert_keep_heap() # 确定每一步add操作都保持堆属性

    def extract(self ): 
        behaupten self._count > 0, 'kann nicht aus einem leeren Heap extrahieren' 
        value = self._elements[0] # Wurzelwert speichern 
        self._count -= 1 
        self._elements[0] = self._elements[self._count ] # Klicken Sie auf die Schaltfläche „root“ und „siftDown“.
        self._siftDown(0) 
        self.assert_keep_heap() 
        Rückgabewert 

    def _siftUp(self, ndx): 
        if ndx > 0: 
            parent = (ndx - 1) // 2 
            # print(ndx, parent) 
            if self._elements[ndx] > self._elements[parent]: # swap 
                self._elements[ndx], self._elements[parent] = self._elements[parent], self._elements[ndx] 
                self._siftUp(parent) # 递归

    def _siftDown(self, ndx): 
        left = 2 * ndx + 1 
        right = 2 * ndx + 2 
        # Bestimmen Sie, welcher Knoten den größeren Wert enthält. 
        most = ndx 
        if (left < self._count and
            self._elements[left] >= self._elements[largest] and 
            self._elements[left] >= self._elements[right]): # Dies steht nicht im Originalbuch. Tatsächlich das, nach dem Sie suchen ist möglicherweise nicht der größte größte 
            = links 
        elif rechts < self._count und self._elements[rechts] >= self._elements[größter]: 
            größter = rechts 
        , wenn größter != ndx: 
            self._elements[ndx], self._elements[größter ] = self._elements[largest], self._elements[ndx] 
            self._siftDown(largest) 

    def __repr__(self): 
        return ' '.join(map(str, self._elements)) 

    def Assert_keep_heap(self): 
        "" „Ich habe diese Funktion hinzugefügt, um zu überprüfen, ob nach jedem Hinzufügen oder Extrahieren die Eigenschaft des maximalen Heaps weiterhin erhalten bleibt. „““ 
        _len = len(self)
        for i in range(0, int((_len-1)/2)): # Interner Knoten (Nicht-Blattknoten) l 
            = 2 * i + 1 
            r = 2 * i + 2, 
            wenn l < _len und r < _len : 
                affirm self._elements[i] >= self._elements[l] and self._elements[i] >= self._elements[r] 

def test_MaxHeap(): 
    """ Unit-Testfall für maximale Heap-Implementierung""" 
    _len = 10 
    h = MaxHeap(_len) 
    for i in range(_len): 
        h.add(i) 
        h.assert_keep_heap() 
    for i in range(_len): # Stellen Sie sicher, dass beim Hinzufügen 
        von Assert 
        jedes Mal die größte Zahl herauskommt h.extract() == _len-i-1 

test_MaxHeap() 

def simpleHeapSort(theSeq): von klein nach groß hinzugefügt :
    „““ Verwenden Sie Ihre eigene Implementierung von MaxHeap, um die Heap-Sortierung zu implementieren, und ändern Sie das ursprüngliche Array direkt, um die Inplace-Sortierung zu implementieren.“
    wenn nicht theSeq: 
        return theSeq 
    _len = len(theSeq) 
    heap = MaxHeap(_len) 
    for i in theSeq: 
        heap.add(i) 
    for i in reversed(range(_len)): 
        theSeq[i] = heap.extract() 
    return theSeq 


def test_simpleHeapSort(): 
    """ Verwenden Sie einige Testfälle, um zu beweisen, dass die implementierte Heap-Sortierung funktionieren kann""" 
    def _is_sorted(seq): 
        for i in range(len(seq)-1): 
            if seq[i] > seq[i+1]: 
                return False 
        return True 

    from random import randint 
    Assert simpleHeapSort([]) == [] 
    for i in range(1000): 
        _len = randint(1, 100) 
        to_sort = []
        for i in range(_len): 
            to_sort.append(randint(0, 100)) 
        simpleHeapSort(to_sort) # Beachten Sie, dass hier In-Place-Sortierung verwendet wird, wodurch das Array direkt geändert wird. 
        Assert _is_sorted(to_sort) 


test_simpleHeapSort()

Kapitel 14: Suchbäume

Eigenschaften für die Suche nach binären Differenzbäumen: für jeden internen Knoten V, 1. Alle Schlüssel, die kleiner als V.key sind, werden im linken Teilbaum von V gespeichert. 2. Alle Schlüssel, die größer als V.key sind, werden im rechten Teilbaum von V gespeichert. Die Durchführung einer In-Order-Traversierung auf dem BST führt zu einer aufsteigenden Schlüsselsequenz.

Klasse _BSTMapNode: 
    __slots__ = ('key', 'value', 'left', 'right') 

    def __init__(self, key, value): 
        self.key = key 
        self.value = value 
        self.left = Keine 
        self.right = Keine 

    def __repr__(self): 
        return '<{}:{}> left:{}, right:{}'.format( 
            self.key, self.value, self.left, self.right) 

    __str__ = __repr__ 


Klasse BSTMap: 
    „““ BST, Baumknoten enthalten Schlüsselnutzlasten. Verwenden Sie BST, um das zuvor mit Hash implementierte Map ADT zu implementieren. 
    Eigenschaften: Für jeden internen Knoten V, 
    1. Für Knoten V werden alle Schlüssel kleiner als V.key gespeichert Linker Teilbaum von V. 
    2. Alle Schlüssel, die größer als V.key sind, werden im rechten Teilbaum von V gespeichert. Wenn 
    Sie BST in der richtigen Reihenfolge durchqueren, erhalten Sie die aufsteigende Schlüsselfolge 
    „““ 
    def __init__(self):
        self._root = Keine
        self._size = 0 
        self._rval = None # Als Rückgabewert von Remove 

    def __len__(self): 
        return self._size 

    def __iter__(self): 
        return _BSTMapIterator(self._root, self._size) 

    def __contains__(self, key ) : 
        return self._bstSearch(self._root, key) is not None 

    def valueOf(self, key): 
        node = self._bstSearch(self._root, key) 
        Assert node is not None, 'Ungültiger Kartenschlüssel.' 
        return node . value 

    def _bstSearch(self, subtree, target): 
        if subtree is None: # Rekursiver Exit, zum Ende des Baums durchlaufen, wenn kein Schlüssel gefunden wird oder der Baum leer ist, 
            return None 
        elif target < subtree.key:
            return self._bstSearch(subtree.left, target) 
        elif target > subtree.key: 
            return self._bstSearch(subtree.right, target) 
        return subtree # Return reference 

    def _bstMinumum(self, subtree): 
        „““ Dem Baum folgen, 
        wenn 
            is None: return 
            subtree 
        else 
        : 
            return subtree._bstMinumum(self, subtree.left) 
    def add( self, key, value): 
        """ Den Wert eines Schlüssels hinzufügen oder ersetzen, O (N) „““ 
        node = self._bstSearch(self._root, key) 
        wenn node nicht None ist:# Wenn der Schlüssel bereits vorhanden ist, aktualisieren Sie den Wert

            node.value = value 
            return False 
        else: # einen neuen Eintrag einfügen 
            self._root = self._bstInsert(self._root, key, value) 
            self._size += 1 
            return True 

    def _bstInsert(self, subtree, key, value): 
            “ “ Neue Knoten werden 
        immer 
        an den Blattknoten des Baums eingefügt 
        . key, value) 
        elif key > subtree.key: 
            subtree.right = self._bstInsert(subtree.right, key, value) 
        # Beachten Sie, dass es hier keine else-Anweisung gibt. Es sollte beurteilt werden, ob es in der Add-Funktion wo eine gibt Es heißt. Wiederholen Sie den Schlüssel 
        und geben Sie den Teilbaum zurück

    def remove(self, key): 
        """ O(N) 
        Es gibt drei Arten gelöschter Knoten: 
        1. Blattknoten: Setzen Sie den Zeiger seines Vaters auf den Knoten direkt auf None 
        2. Der Knoten hat ein untergeordnetes Element: delete Nach dem Knoten , der Vater zeigt auf ein geeignetes Kind 
        3 des Knotens. Der Knoten hat zwei Kinder: 
            (1) Suchen Sie den zu löschenden Knoten N und seinen Nachfolger S (den nächsten Knoten nach der Durchquerung in der Reihenfolge) 
            (2) Kopieren Sie den Schlüssel von S bis N (3) Löschen 
        Sie den Nachfolger S aus dem 
        rechten Teilbaum von N (d. h 
        . 
            den kleinsten im rechten Teilbaum von N) (self. _root, key) 
        self._size -= 1 
        return self._rval 
    def _bstRemove(self, subtree, target): 
        # Suche nach dem Element im Baum 
        , wenn subtree None ist: 
            return subtree 
        elif target < subtree.Schlüssel:

            subtree.left = self._bstRemove(subtree.left, target) 
            return subtree 
        elif target > subtree.key: 
            subtree.right = self._bstRemove(subtree.right, target) 
            return subtree 

        else: # hat den Knoten gefunden, der das Element 
            self enthält. _rval = subtree.value, 
            wenn subtree.left None und subtree.right None ist: 
                # 叶子node 
                return None 
            elif subtree.left is None or subtree.right is None: 
                # 有一个孩子节点
                if subtree.left is not None: 
                    return subtree.left 
                sonst:
                    return subtree.right 
            else: # Es gibt zwei untergeordnete Knoten 
                successor = self._bstMinumum(subtree.right) 
                subtree.key = successor.key 
                subtree.value = successor.value 
                subtree.right = self._bstRemove(subtree.right, successor. key ) 
                return subtree 

    def __repr__(self): 
        return '->'.join([str(i) for i in self]) 

    def Assert_keep_bst_property(self, subtree): 
        """ Diese Funktion wurde geschrieben, um das Hinzufügen und Löschen zu überprüfen Operationen werden immer beibehalten. Die Eigenschaften von bst „““, 
        wenn subtree None ist: 
            Rückgabe 
        , wenn subtree.left nicht None und subtree.right nicht None ist:
            Assert subtree.left.value <= subtree.value 
            Assert subtree.right.value >= subtree.value 
            self.assert_keep_bst_property(subtree.left) 
            self.assert_keep_bst_property(subtree.right) 

        elif subtree.left ist None und subtree.right ist nicht Keine: 
            behaupten subtree.right.value >= subtree.value 
            self.assert_keep_bst_property(subtree.right) 

        elif subtree.left ist nicht None und subtree.right ist None: 
            Assert subtree.left.value <= subtree.value 
            self.assert_keep_bst_property( subtree.left) 


Klasse _BSTMapIterator: 
    def __init__(self, root, size): 
        self._theKeys = Array(size)
        self._curItem = 0 
        self._bstTraversal(root) 
        self._curItem = 0 

    def __iter__(self): 
        return self 

    def __next__(self): 
        if self._curItem < len(self._theKeys): 
            key = self._theKeys[self. _curItem] 
            self._curItem += 1 
            Rückgabeschlüssel 
        sonst: 
            StopIteration erhöhen 

    def _bstTraversal(self, subtree): 
        wenn subtree nicht None ist: 
            self._bstTraversal(subtree.left) 
            self._theKeys[self._curItem] = subtree.key 
            self. _curItem += 1 
            self._bstTraversal(subtree.right) 


def test_BSTMap():
    l = [60, 25, 100, 35, 17, 80] 
    bst = BSTMap() 
    für i in l: 
        bst.add(i) 

def test_HashMap(): 
    „““ Zuvor verwendet, um die mit Hash implementierte Karte zu testen, ändern Test für Map implementiert in BST „““ 
    # h = HashMap() 
    h = BSTMap() 
    Assert len(h) == 0 
    h.add('a', 'a') 
    Assert h.valueOf('a') = = 'a' 
    Assert len(h) == 1 

    a_v = h.remove('a') 
    Assert a_v == 'a' 
    Assert len(h) == 0 

    h.add('a', 'a') 
    h .add('b', 'b') 
    Assert len(h) == 2 
    Assert h.valueOf('b') == 'b' 
    b_v = h.entferne('b') 
    behaupten b_v == 'b' 
    behaupten len(h) == 1 
    h.remove('a')

    behaupten len(h) == 0 

    _len = 10 
    für i im Bereich(_len): 
        h.add(str(i), i) 
    behaupten len(h) == _len 
    für i im Bereich(_len): 
        behaupten str(i ) in h 
    für i in range(_len): 
        print(len(h)) 
        print('bef', h) 
        _ = h.remove(str(i)) 
        Assert _ == i 
        print('aft', h) 
        print(len(h)) 
    affirm len(h) == 0 

test_HashMap()

Ich denke du magst

Origin blog.csdn.net/gongdiwudu/article/details/118111542
Empfohlen
Rangfolge