class DictAVL(DictBinaryTree):
    def __init__(self):
        self.tree = BinaryTree(
        )  #Node's info now is a triple [key,value,height]

    def height(self, node):
        """Restituisce l'altezza del sottoalbero che ha come radice node.
        Ovvero il numero di livelli di discendenza di quel nodo."""
        if node == None:
            return -1  #aiuta a calcolare il balance factor
        return node.info[2]

    def setHeight(self, node, h):
        """Metodo per settare l'altezza del nodo node al valore h."""
        if node != None:
            node.info[2] = h

    def balanceFactor(self, node):
        """Permette di calcolare il fattore di bilanciamento del nodo node."""
        if node == None:
            return 0
        return self.height(node.leftSon) - self.height(node.rightSon)

    def updateHeight(self, node):
        """Permette di aggiornare l'altezza del nodo node al valore uguale a:
        massima altezza tra le altezza dei due figli, a cui deve essere aggiunto 1."""
        if node != None:
            self.setHeight(
                node,
                max(self.height(node.leftSon), self.height(node.rightSon)) + 1)

# Balancing

    def rightRotation(self, node):
        leftSon = node.leftSon
        node.info, leftSon.info = leftSon.info, node.info

        rtree = self.tree.cutRight(node)
        ltree = self.tree.cutLeft(node)
        ltree_l = ltree.cutLeft(leftSon)
        ltree_r = ltree.cutRight(leftSon)

        ltree.insertAsRightSubTree(ltree.root, rtree)
        ltree.insertAsLeftSubTree(ltree.root, ltree_r)
        self.tree.insertAsRightSubTree(node, ltree)
        self.tree.insertAsLeftSubTree(node, ltree_l)

        self.updateHeight(node.rightSon)
        self.updateHeight(node)

    def leftRotation(self, node):
        rightSon = node.rightSon
        node.info, rightSon.info = rightSon.info, node.info

        rtree = self.tree.cutRight(node)
        ltree = self.tree.cutLeft(node)
        rtree_l = rtree.cutLeft(rightSon)
        rtree_r = rtree.cutRight(rightSon)

        rtree.insertAsLeftSubTree(rtree.root, ltree)
        rtree.insertAsRightSubTree(rtree.root, rtree_l)
        self.tree.insertAsLeftSubTree(node, rtree)
        self.tree.insertAsRightSubTree(node, rtree_r)

        self.updateHeight(node.leftSon)
        self.updateHeight(node)

    def rotate(self, node):
        """Partendo dal nodo node, riesce a capire quale e' il tipo
        di rotazione da effettuare in base al fattore di bilanciamento
        del nodo e de suoi figli."""
        balFact = self.balanceFactor(node)
        if balFact == 2:  #altezza figlio sinistro di node e' piu' grande di 2 rispetto al figlio destro
            if self.balanceFactor(node.leftSon) >= 0:  #sbilanciamento SS
                self.rightRotation(node)
            else:  #sbilanciamento SD
                self.leftRotation(node.leftSon)
                self.rightRotation(node)
        elif balFact == -2:  #altezza figlio destro di node e' piu' grande di 2 rispetto al figlio sinistro
            if self.balanceFactor(node.rightSon) <= 0:  #sbilanciamento DD
                self.leftRotation(node)
            else:  #sbilanciamento DS
                self.rightRotation(node.rightSon)
                self.leftRotation(node)

#INSERTION: quite the same as for dictBinaryTree. We have only to add the ability to manage nodes' height.

    def balInsert(self, newNode):
        curr = newNode.father
        while curr != None:
            if abs(self.balanceFactor(curr)) >= 2:
                break  #stop the height update at the first unbalanced predecessor.
            else:
                self.updateHeight(curr)
                curr = curr.father
        if curr != None:
            self.rotate(curr)

    def insert(self, key, value):
        newt = BinaryTree(BinaryNode([
            key, value, 0
        ]))  #Primo cambiamento e' tripletta al posto della coppia key value

        if self.tree.root == None:
            self.tree.root = newt.root
        else:
            curr = self.tree.root
            pred = None
            while curr != None:
                pred = curr
                if key <= self.key(curr):
                    curr = curr.leftSon
                else:
                    curr = curr.rightSon

            if key <= self.key(pred):
                self.tree.insertAsLeftSubTree(pred, newt)
            else:
                self.tree.insertAsRightSubTree(pred, newt)
            self.balInsert(
                newt.root
            )  #secondo cambiamento e' la chiamata del metodo per bilanciare l'albero


#DELETION: quite the same as for dictBinaryTree. We have only to add the ability to manage nodes' height.

    def balDelete(self, removedNode):
        curr = removedNode.father
        while curr != None:  #more than one may need to be rebalanced
            if abs(self.balanceFactor(curr)) == 2:
                self.rotate(curr)
            else:
                self.updateHeight(curr)
            curr = curr.father

    def cutOneSonNode(self, node):  #contrai un nodo con un singolo figlio
        son = None
        if node.leftSon != None:
            son = node.leftSon
        elif node.rightSon != None:
            son = node.rightSon

        if son == None:
            self.tree.cut(node)  #is a leaf
        else:
            node.info, son.info = son.info, node.info  #swap info
            nt = self.tree.cut(son)
            self.tree.insertAsLeftSubTree(node, nt.cut(son.leftSon))
            self.tree.insertAsRightSubTree(node, nt.cut(son.rightSon))

        self.balDelete(node)  #This is the only change

    def delete(self, key):
        toRemove = self.searchNode(key)
        if toRemove != None:
            if toRemove.leftSon == None or toRemove.rightSon == None:
                self.cutOneSonNode(toRemove)
            else:
                maxLeft = self.maxKeySon(toRemove.leftSon)
                toRemove.info, maxLeft.info = maxLeft.info, toRemove.info
                #Avendo effettuato lo swap di tutto il campo info, si sono scambiate
                #anche le altezze dei nodi che invece dovevano rimanere uguali a prima
                #per tale motivo con queste tre righe di codice si ripristinano le
                #corrette altezze
                th = self.height(toRemove)
                self.setHeight(toRemove, self.height(maxLeft))
                self.setHeight(maxLeft, th)

                self.cutOneSonNode(maxLeft)
Esempio n. 2
0
class DictBinaryTree:
    """Un albero binario di ricerca e' un albero che soddisfa le seguenti proprieta':
    1. ogni nodo v contiene un valore (info[1]) cui e' associata una chiave (info[0])
        presa da n dominio totalmente ordinato.
    2. Le chiavi nel sottoalbero sinistro di v sono <= chiave(v).
    3. Le chiavi nel sottoalbero destro di v sono >= chiave(v)."""
    def __init__(self):
        self.tree = BinaryTree()  # node's info now is a list [key, value]

    def key(self, node):
        """Permette di ritornare la chiave associata ad un nodo"""
        if node == None:
            return None
        return node.info[0]

    def value(self, node):
        """Permette di ritornare il valore associato ad un nodo"""
        if node == None:
            return None
        return node.info[1]

    def maxKeySon(self, root):
        """Permette di ottenere il nodo con chiave piu' grande,
        partendo dal nodo root. Il nodo con chiave
        piu' grande e' quello che si trova piu' a destra possibile"""
        curr = root
        while curr.rightSon != None:
            curr = curr.rightSon
        return curr

    def isLeftSon(self, node):
        """Permette di capire se il nodo node è il figlio sinistro del proprio
        padre."""
        if node == node.father.leftSon:
            return True
        return False

    def search(self, key):
        """Permette di ottenere il valore associato alla chiave key presente
        all'interno dell'albero"""
        node = self.searchNode(key)
        return self.value(node)

    def searchNode(self, key):
        """Permette di ricercare il nodo con chiave key all'interno del dizionario.
        Ritorna il nodo, oppure None se non c'è nodo associato a key"""
        if self.tree.root == None:
            return None

        curr = self.tree.root
        while curr != None:
            ck = self.key(curr)
            if key == ck:
                return curr

            if key < ck:
                curr = curr.leftSon
            else:
                curr = curr.rightSon

        return None

    def insert(self, key, value):
        """Permette di inserire un valore all'interno del dizionario
        nella maniera corretta"""
        pair = [key, value]
        newt = BinaryTree(BinaryNode(pair))

        if self.tree.root == None:
            self.tree.root = newt.root
        else:
            curr = self.tree.root  #nodo corrente
            pred = None  #nodo precedente che abbiamo analizzato
            while curr != None:
                pred = curr
                if key <= self.key(
                        curr
                ):  #chiave che sto inserendo e' piu piccola di quella corrente
                    curr = curr.leftSon
                else:
                    curr = curr.rightSon

            if key <= self.key(pred):
                self.tree.insertAsLeftSubTree(pred, newt)
            else:
                self.tree.insertAsRightSubTree(pred, newt)

    def cutOneSonNode(self, node):  #contrai un nodo con un singolo figlio
        """Permette di cancellare un nodo dall'albero, sapendo che il nodo
        che si sta cancellando ha al massimo un solo figlio."""
        son = None
        if node.leftSon != None:
            son = node.leftSon
        elif node.rightSon != None:
            son = node.rightSon

        if son == None:
            self.tree.cut(node)  #is a leaf
        else:
            node.info, son.info = son.info, node.info  #swap info
            nt = self.tree.cut(son)
            self.tree.insertAsLeftSubTree(node, nt.cut(son.leftSon))
            self.tree.insertAsRightSubTree(node, nt.cut(son.rightSon))

    def delete(self, key):
        """Permette di cancellare il nodo appartenente all'albero con
        chiave key"""
        toRemove = self.searchNode(key)
        if toRemove != None:
            if toRemove.leftSon == None or toRemove.rightSon == None:  #sto rimuovendo un nodo che ha 0 o 1 figlio
                self.cutOneSonNode(toRemove)
            else:  # sto rimuovendo un nodo che ha due nodi figli
                maxLeft = self.maxKeySon(
                    toRemove.leftSon)  #predecessore del nodo da rimuovere
                toRemove.info, maxLeft.info = maxLeft.info, toRemove.info  # scambio il contenuto informativo dei nodi
                self.cutOneSonNode(
                    maxLeft
                )  #adesso so che maxLeft non ha figli destri e posso applicare la cutOneSonNode