Utiliser Python pour implémenter des structures de données de base [04/4]

illustrer

        Si vous avez besoin d'utiliser ces connaissances mais que vous ne les possédez pas, cela sera frustrant et pourrait conduire à un refus de l'entretien. Que vous passiez quelques jours à « faire du blitz » ou que vous utilisiez un temps fragmenté pour continuer à apprendre, cela vaut la peine de travailler sur la structure des données. Alors, quelles sont les structures de données en Python ? Des listes, des dictionnaires, des ensembles et... des piles ? Python a-t-il une pile ? Cette série d'articles donnera des pièces de puzzle détaillées.


Chapitre 13 : Arbre binaire

L'arbre binaire : Arbre binaire, chaque nœud n'a que deux nœuds enfants.

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


# 三种third-first遍历
def preorderTrav(subtree): 
    """ 先(根)序遍历"" " 
    si le sous-arbre n'est pas Aucun : 
        print(subtree.data) 
        preorderTrav(subtree.left) 
        preorderTrav(subtree.right) 


def inorderTrav(subtree) : 
    """ 中(根)序遍历""" 
    si le sous-arbre n'est pas Aucun : 
        preorderTrav (sous-arbre.left) 
        print(subtree.data) 
        preorderTrav(sous-arbre.right) 


def postorderTrav(sous-arbre) :
    """ Traversée de l'ordre post (racine)""" 
    si le sous-arbre n'est pas Aucun :
        preorderTrav(subtree.left) 
        self.element = data
        preorderTrav(subtree.right) 
        print(subtree.data) 


# 宽度优先遍历(bradth-First Traversal) : 一层一层遍历, 使用queue 
def widthFirstTrav(bintree) : 
    from queue import Queue # py3 
    q = Queue() 
    q. put(bintree) 
    tant que ce n'est pas q.empty() : 
        node = q.get() 
        print(node.data) 
        si node.left n'est pas None : 
            q.put(node.left) 
        si node.right n'est pas None : 
            q .put(node.right) 


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

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

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

à partir de la file d'attente d'importation 
Classe de file d'attente ExpressionTree : 
    """ 
    Arbre d'expression : un arbre binaire dans lequel les opérateurs sont stockés dans des nœuds internes et les opérandes sont stockés dans des nœuds feuilles. (L'arbre de symboles est vraiment difficile à saisir) * / \ + 
        - 
       / 
      \ 
     / \ 
     9 3 8 4 
    ( 9+3) * (8-4) 

    Le type de données abstrait Expression Tree peut implémenter des opérateurs binaires 
    ExpressionTree(expStr) : chaîne utilisateur comme paramètre de constructeur 
    évaluer(varDict) : évalue l'expression et renvoie le résultat numérique
    toString() : construit et renvoie une représentation sous forme de chaîne de l'expression Utilisation 

    : 
        vars = {'a': 5, 'b': 12} 12} 
        expTree = ExpressionTree("(a/(b-3))") 
        print('Le résultat = ', expTree.evaluate(vars))
    """ 

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

    def évaluer(self, varDict) : 
        return self._evalTree(self._expTree, varDict) 

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

    def _buildString(self, treeNode) : 
        """ chez un enfant Ajoutez des parenthèses avant que l'arborescence ne soit parcourue, ajoutez des parenthèses droites """ après la traversée du sous-arbre 
        # print(treeNode) 
        si treeNode.left est None et treeNode.right est None : 
            return str(treeNode.élément) #Le nœud feuille est l'opérande et renvoie directement 
        else :
            expStr = '(' 
            expStr += self._buildString(treeNode.left) 
            expStr += str(treeNode.element) 
            expStr += self._buildString(treeNode.right) 
            expStr += ')'  
            return expStr

    def _evalTree(self, sous-arbre, varDict ): 
        # Est-ce un nœud feuille ? Si oui, cela signifie que c'est un opérande et renvoie directement 
        si subtree.left vaut None et subtree.right vaut None : 
            # L'opérande est-il un numéro légal 
            si subtree.element >= '0' et subtree.element <= '9' : 
                return int(subtree.element) 
            else : # L'opérande est une variable 
                assert subtree.element dans varDict, 'variable invalide.' 
                return varDict[subtree.element] 
        else :L'opérateur # évalue ses sous-expressions
            lvalue = self._evalTree(subtree.left, varDict)  
            rvalue = self._evalTree(subtree.right, varDict) 
            print(subtree.element) 
            return self._computeOp(lvalue, subtree.element,valeur)

    def _computeOp(self, left, op, right) : 
        assert op 
        op_func = { 
            '+' : lambda gauche, droite : gauche + droite, # ou opérateur d'importation, Operator.add 
            '-' : lambda gauche, droite : gauche - droite, 
            '*' : lambda gauche, droite : gauche * droite, 
            '/' : lambda gauche, droite : gauche/droite, 
            '%' : lambda gauche, droite : gauche % right, 
        } 
        return op_func[op](left, right) 

    def _buildTree(self, expStr) : 
        expQ = Queue() 
        pour le jeton dans expStr : # 遍历表达式字符串的每个字符
            expQ.put(token)
        self._expTree = _ExpTreeNode(None) # Utiliser la racine 
        self._recBuildTree(self._expTree, expQ) 

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

            # le prochain jeton sera un opérateur : + = * / % 
            curNode.element = expQ.get() 
            curNode.right = _ExpTreeNode(None) 
            self._recBuildTree(curNode .right, expQ) 

            # le prochain jeton sera ')', retirez-le 
            expQ.get() 

        sinon : # le jeton est un chiffre qui doit être converti en un entier. 
            curNode.element = token 


vars = {'a' : 5, 'b' : 12} 
expTree = ExpressionTree("((2*7)+8)") 
print(expTree) 
print('Le résultat = ', expTree. évaluer(vars))

Tas : L’une des applications les plus directes des arbres binaires consiste à implémenter des tas. Le tas est un arbre binaire complet.Les valeurs des nœuds non-feuilles du plus grand tas sont plus grandes que celles des enfants et les valeurs des nœuds non-feuilles du plus petit tas sont plus petites que les enfants. Python dispose d'un module heapq intégré pour nous aider à implémenter des opérations sur le tas, comme l'utilisation du module heapq intégré pour implémenter le tri par tas :

# Utilisez le heapq intégré de python pour implémenter le tri par tas 
def heapsort(iterable) : 
    depuis heapq import heappush, heappop 
    h = [] 
    pour la valeur dans itérable : 
        heappush(h, value) 
    return [heappop(h) for i in range(len (h) ))]

Cependant, généralement lors de l'implémentation d'un tas, celui-ci n'est pas réellement implémenté en comptant les nœuds, mais en utilisant des tableaux, ce qui est plus efficace. Pourquoi peut-il être implémenté avec un tableau ? En raison de la nature d'un arbre binaire complet, la relation entre les indices peut être utilisée pour représenter la relation entre les nœuds. Cela a été expliqué dans la docstring de MaxHeap.

classe MaxHeap : 
    """ 
    Heaps : 
    arbre binaire complet. Les valeurs des nœuds non-feuilles du tas maximum sont plus grandes que les enfants, et les valeurs des nœuds non-feuilles du tas minimum sont inférieures à les enfants. Le tas 
    contient deux propriétés, la propriété d'ordre et la propriété de forme (un arbre binaire complet), lors de l'insertion 
    d'un nouveau nœud, conservez toujours ces deux attributs. 
    Opération d'insertion : conserve les attributs du tas et complète les attributs de l'arbre binaire, l'opération de tri maintient les attributs du tas. 
    Opération d'extraction : obtenir uniquement les données du nœud racine et convertir l'arborescence. Une fois le nœud inférieur droit copié sur le nœud racine, l'opération de tri conserve l'attribut du tas. Utilisez un tableau pour implémenter le tas. À partir 

    de le nœud racine, numérotez chaque nœud de haut en bas de gauche à droite. Selon 
    les propriétés d'un arbre binaire complet, définissez un nœud i, et les numéros de ses nœuds parent et enfant sont : 
        parent = (i-1) / / 2 
        left = 2 * i + 1 
        rgiht = 2 * i + 2 
    L'utilisation d'un tableau pour implémenter un tas est plus efficace et permet d'économiser de l'argent. L'utilisation de la mémoire des nœuds d'arborescence peut également éviter les opérations de pointeur complexes et réduire 
    la difficulté de débogage. 

    "" " 

    def __init__(self, maxSize): 
        self._elements = Array(maxSize) # Array ADT implémenté au chapitre 2
        self._count = 0 

    def __len__(self) : 
        return self._count 

    defcapacity(self) : 
        return len(self._elements) 

    def add(self, value) : 
        assert self._count < self.capacity(), 'ne peut pas ajouter au tas complet' 
        self._elements[self._count] = value 
        self._count += 1 
        self._siftUp(self._count - 1) 
        self.assert_keep_heap() # 确定每一步add操作都保持堆属性

    def extract(self ): 
        affirmer self._count > 0, 'impossible d'extraire d'un tas vide' 
        value = self._elements[0] # enregistrer la valeur racine 
        self._count -= 1 
        self._elements[0] = self._elements[self._count ] # 最右下的节点放到root et siftDown
        self._siftDown(0) 
        self.assert_keep_heap() 
        valeur de retour 

    def _siftUp(self, ndx) : 
        si 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) # et 

    def _siftDown(self, ndx) : 
        gauche = 2 * ndx + 1 
        droite = 2 * ndx + 2 
        # déterminer quel nœud contient la plus grande valeur 
        la plus grande = ndx 
        si (gauche < self._count et
            self._elements[left] >= self._elements[largest] et 
            self._elements[left] >= self._elements[right]): # Ceci n'est pas écrit dans le livre original. En fait, celui que vous recherchez peut ne pas être le plus grand le plus grand 
            = gauche 
        elif droite < self._count et self._elements[right] >= self._elements[largest] : 
            plus grand = droite 
        si le plus grand != ndx : 
            self._elements[ndx], self._elements[largest ] = self._elements[largest], self._elements[ndx] 
            self._siftDown(largest) 

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

    def assert_keep_heap(self): 
        "" " J'ai ajouté cette fonction pour vérifier qu'après chaque ajout ou extraction, la propriété du tas maximum est toujours conservée """ 
        _len = len(self)
        for i in range(0, int((_len-1)/2)): # Nœud interne (nœud non-feuille) l 
            = 2 * i + 1 
            r = 2 * i + 2 
            si l < _len et r < _len : 
                assert self._elements[i] >= self._elements[l] et self._elements[i] >= self._elements[r] 

def test_MaxHeap(): 
    """ Scénario de test unitaire pour une implémentation maximale du tas """ 
    _len = 10 
    h = MaxHeap(_len) 
    for i in range(_len): 
        h.add(i) 
        h.assert_keep_heap() 
    for i in range(_len): 
        # Assurez-vous que le plus grand nombre ressort à chaque fois, lors de l'ajout d'une 
        assertion h.extract() == _len-i-1 

test_MaxHeap() 

def simpleHeapSort(theSeq) : ajouté de petit à grand :
    """ Utilisez votre propre implémentation de MaxHeap pour implémenter le tri par tas et modifiez directement le tableau d'origine pour implémenter le tri sur place """
    sinon theSeq : 
        renvoie theSeq 
    _len = len(theSeq) 
    heap = MaxHeap(_len) 
    pour i dans theSeq : 
        heap.add(i) 
    pour i in reverse(range(_len)) : 
        theSeq[i] = heap.extract() 
    return theSeq 


def test_simpleHeapSort() : 
    """ Utilisez quelques cas de test pour prouver que le tri par tas implémenté peut fonctionner """ 
    def _is_sorted(seq) : 
        for i in range(len(seq)-1) : 
            if seq[i] > seq[i+1] : 
                return False 
        return True 

    à partir d'une importation aléatoire randint 
    assert simpleHeapSort([]) == [] 
    pour i in range(1000) : 
        _len = randint(1, 100) 
        to_sort = []
        for i in range(_len): 
            to_sort.append(randint(0, 100)) 
        simpleHeapSort(to_sort) # Notez que le tri sur place est utilisé ici, modifiant directement le tableau 
        assert _is_sorted(to_sort) 


test_simpleHeapSort()

Chapitre 14 : Arborescences de recherche

Propriétés de recherche d'arbre de différences binaires : pour chaque nœud interne V, 1. Toutes les clés plus petites que V.key sont stockées dans le sous-arbre gauche de V. 2. Toutes les clés supérieures à V.key sont stockées dans le sous-arbre droit de V. Effectuer un parcours dans l'ordre sur le BST entraînera une séquence de touches ascendante.

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

    def __init__(self, key, value) : 
        self.key = clé 
        self.value = valeur 
        self.left = Aucun 
        self.right = Aucun 

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

    __str__ = __repr__ 


classe BSTMap : 
    """ BST, les nœuds de l'arborescence contiennent des charges utiles clés. Utilisez BST pour implémenter le Map ADT précédemment implémenté avec un hachage. 
    Propriétés : Pour chaque nœud interne V, 
    1. Pour le nœud V, toutes les clés inférieures à V.key sont stockées dans le sous-arbre gauche de V. 
    2. Toutes les clés supérieures à V.key sont stockées dans le sous-arbre droit de V. 
    Effectuer un parcours dans l'ordre sur BST obtiendra la séquence de touches ascendante 
    """ 
    def __init__(self) :
        self._root = Aucun
        self._size = 0 
        self._rval = None # Comme valeur de retour de 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) n'est pas Aucun 

    def valueOf(self, key): 
        node = self._bstSearch(self._root, key) 
        assert node n'est pas Aucun, 'Clé de mappage invalide.' 
        return node .value 

    def _bstSearch(self, subtree, target) : 
        si le sous-arbre est Aucun : # Sortie récursive, traverse jusqu'au bas de l'arborescence si aucune clé n'est trouvée ou si l'arborescence est vide 
            return None 
        elif target < subtree.key :
            return self._bstSearch(subtree.left, target) 
        elif target > subtree.key : 
            return self._bstSearch(subtree.right, target) 
        return subtree # Renvoie la référence 

    def _bstMinumum(self, subtree) : 
        """ Suivre l'arborescence 
        Si 
            is None : return 
            subtree 
        else 
        : 
            return subtree._bstMinumum(self, subtree.left) 
    def add( self, key, value): 
        """ Ajouter ou remplacer la valeur d'une clé, O (N) """ 
        node = self._bstSearch(self._root, key) 
        si le nœud n'est pas None :# si la clé existe déjà, mettre à jour la valeur

            node.value = value 
            return False 
        else : # insérer une nouvelle entrée 
            self._root = self._bstInsert(self._root, key, value) 
            self._size += 1 
            return True 

    def _bstInsert(self, subtree, key, value) : 
        """ Les nouveaux nœuds sont toujours insérés aux nœuds feuilles de l'arbre """ 
        si le sous-arbre est Aucun : 
            subtree = _BSTMapNode(key, value) 
        elif key < subtree.key: 
            subtree.left = self._bstInsert(subtree.left , key, value) 
        elif key > subtree.key: 
            subtree.right = self._bstInsert(subtree.right, key, value) 
        # Notez qu'il n'y a pas d'instruction else ici. Il faut juger s'il y en a une dans la fonction add où il est appelé. Répéter 
        le sous-arbre de retour de clé

    def remove(self, key) : 
        """ O(N) 
        Il existe trois types de nœuds supprimés : 
        1. Nœud feuille : définit directement le pointeur de son père vers le nœud sur Aucun 
        2. Le nœud a un enfant : delete Après le nœud , le père désigne un enfant approprié 
        3 du nœud. Le nœud a deux enfants : 
            (1) Trouver le nœud N à supprimer et son successeur S (le nœud suivant après le parcours dans l'ordre) 
            (2) Copier la clé de S à N 
            (3) Supprimez le successeur S du sous-arbre droit de N (c'est-à-dire le plus petit du sous-arbre droit de N) "" " 
        affirme 
        la clé en soi, 'clé de mappage invalide' 
        self._root = self._bstRemove (self. _root, key) 
        self._size -= 1 
        return self._rval 

    def _bstRemove(self, subtree, target) : 
        # recherche l'élément dans l'arborescence 
        si le sous-arbre est Aucun : 
            renvoie le sous-arbre 
        elif target < sous-arbre.clé:
            subtree.left = self._bstRemove(subtree.left, target) 
            return subtree 
        elif target > subtree.key : 
            subtree.right = self._bstRemove(subtree.right, target) 
            return subtree 

        else : # trouvé le nœud contenant l'élément 
            self. _rval = subtree.value 
            si subtree.left est None et subtree.right est None : 
                # 叶子node 
                return None 
            elif subtree.left est None ou subtree.right est None : 
                # 有一个孩子节点
                if subtree.left n'est pas None : 
                    return sous-arbre.left 
                sinon :
                    return subtree.right 
            else : # Il y a deux nœuds enfants 
                successeur = self._bstMinumum(subtree.right) 
                subtree.key = successeur.key 
                subtree.value = successeur.value 
                subtree.right = self._bstRemove(subtree.right, successeur. key ) 
                return subtree 

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

    def assert_keep_bst_property(self, subtree): 
        """ Cette fonction est écrite pour vérifier que l'ajout et la suppression les opérations sont toujours maintenues Les propriétés de bst """ 
        si le sous-arbre est Aucun : 
            renvoie 
        si subtree.left n'est pas Aucun et subtree.right n'est pas Aucun :
            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 est None et subtree.right ne l'est pas Aucun : 
            assert subtree.right.value >= subtree.value 
            self.assert_keep_bst_property(subtree.right) 

        elif subtree.left n'est pas None et subtree.right est None : 
            assert subtree.left.value <= subtree.value 
            self.assert_keep_bst_property( subtree.left) 


classe _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) : 
        si self._curItem < len(self._theKeys) : 
            key = self._theKeys[self. _curItem] 
            self._curItem += 1 
            touche de retour 
        sinon : 
            augmentez StopIteration 

    def _bstTraversal(self, subtree) : 
        si le sous-arbre n'est pas Aucun : 
            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() 
    for i in l: 
        bst.add(i) 

def test_HashMap(): 
    """ Précédemment utilisé pour tester la carte implémentée avec hachage, changement Test de carte implémenté dans 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.remove('b') 
    assert b_v == 'b' 
    assert len(h) == 1 
    h.remove('a')

    assert len(h) == 0 

    _len = 10 
    pour i dans range(_len) : 
        h.add(str(i), i) 
    assert len(h) == _len 
    pour i dans range(_len) : 
        assert str(i) ) in h 
    for i in range(_len): 
        print(len(h)) 
        print('bef', h) 
        _ = h.remove(str(i)) 
        assert _ == i 
        print('aft', h) 
        print(len(h)) 
    assert len(h) == 0 

test_HashMap()

Je suppose que tu aimes

Origine blog.csdn.net/gongdiwudu/article/details/118111542
conseillé
Classement