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)
예제 #2
0
class DictAVL(DictBinaryTree):
    def __init__(self):
        self.tree = BinaryTree()  #I nodi ora sono una lista [chiave,altezza]

    def height(self, node):
        if node == None:
            return -1
        return node.info[1]

    def setHeight(self, node, h):
        if node != None:
            node.info[1] = h

    def balanceFactor(self, node):
        if node == None:
            return 0
        return self.height(node.leftSon) - self.height(node.rightSon)

    def updateHeight(self, node):
        if node != None:
            self.setHeight(
                node,
                max(self.height(node.leftSon), self.height(node.rightSon)) + 1)

#BILANCIAMENTO

    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):
        balFact = self.balanceFactor(node)
        #controlla il fattore di bilanciamento per scegliere la rotazione opportuna
        if balFact == 2:
            if self.balanceFactor(node.leftSon) >= 0:
                self.rightRotation(node)
            else:
                self.leftRotation(node.leftSon)
                self.rightRotation(node)
        elif balFact == -2:
            if self.balanceFactor(node.rightSon) <= 0:
                self.leftRotation(node)
            else:
                self.rightRotation(node.rightSon)
                self.leftRotation(node)

#INSERIMENTO

    def balInsert(self, newNode):
        curr = newNode.father
        while curr != None:
            if abs(self.balanceFactor(curr)) >= 2:
                break  #smette di aggiornare le altezze appena trova un nodo da ruotare
            else:
                self.updateHeight(curr)
                curr = curr.father
        if curr != None:
            self.rotate(curr)

    def insert(self, stringa):
        #crea un nuovo nodo binario
        new_node = BinaryNode([stringa, 0])
        #verifica se l'albero è vuoto
        if self.tree.root == None:
            self.tree.root = new_node
        else:
            current_node = self.tree.root
            inserted = False
            while inserted == False:
                if new_node.info[0] <= current_node.info[0]:
                    if current_node.leftSon == None:
                        new_node.father = current_node
                        current_node.leftSon = new_node
                        inserted = True
                    else:
                        current_node = current_node.leftSon
                else:
                    if current_node.rightSon == None:
                        new_node.father = current_node
                        current_node.rightSon = new_node
                        inserted = True
                    else:
                        current_node = current_node.rightSon
        self.balInsert(new_node)

#CANCELLAZIONE:

    def balDelete(self, removedNode):
        curr = removedNode.father
        while curr != None:
            #ribilanciamento e modifica altezza a seguito della cancellazione
            if abs(self.balanceFactor(curr)) == 2:
                self.rotate(curr)
            else:
                self.updateHeight(curr)
            curr = curr.father

    def delete(self, stringa):
        #verifico se il il nodo che devo eliminare esiste nell'albero
        node_to_delete = self.searchNode(stringa)
        if node_to_delete != None:
            #se il nodo che cerco è presente ho tre casi
            if node_to_delete.rightSon == None and node_to_delete.leftSon == None:
                self.delete_if_noSon(node_to_delete)
                return True
            elif node_to_delete.rightSon == None or node_to_delete.leftSon == None:
                self.delete_if_oneSon(node_to_delete)
                return True
            else:
                self.delete_if_twoSon(node_to_delete)
                return True
        return None

# per eliminare un nodo lo scollego in un solo verso, cioè scollego il padre dal figlio, non il figlio dal padre, collegamento utile nella rotazione

    def delete_if_noSon(self, node):
        node_to_delete = node
        #verifica se il nodo da rimuovere è la radice
        if self.tree.root == node_to_delete:
            self.tree.root = None
            return
        else:
            pred = node_to_delete.father
            #verifica se il nodo da rimuovere è figlio destro i sinistro del padre, e lo scollega
            if pred.leftSon == node_to_delete:
                pred.leftSon = None
            else:
                pred.rightSon = None
        self.balDelete(node_to_delete)  #ribilancia

# per eliminare un nodo lo scollego in un solo verso, cioè scollego il nodo dal figlio, non il figlio dal nodo
# collegamento utile nella rotazione

    def delete_if_oneSon(self, node):
        node_to_delete = node
        #verifica se il nodo da rimuovere è la radice
        if self.tree.root == node_to_delete:
            if node_to_delete.leftSon == None:
                self.tree.root = node_to_delete.rightSon
                node_to_delete.rightSon = None
            else:
                self.tree.root = node_to_delete.leftSon
                node_to_delete.leftSon = None
            self.tree.root.father = None

        else:
            pred = node_to_delete.father
            #verifica se l'unico figlio del nodo da eliminare è il destro o il sinistro
            if node_to_delete.leftSon == None:
                rs = node_to_delete.rightSon
                #verifica se il nodo da cancellare è figlio destro o sinistro del padre
                if pred.leftSon == node_to_delete:
                    pred.leftSon = rs
                elif pred.rightSon == node_to_delete:
                    pred.rightSon = rs
                rs.father = pred
                node_to_delete.rightSon = None
            elif node_to_delete.rightSon == None:
                ls = node_to_delete.leftSon
                #verifica se il nodo da cancellare è figlio destro o sinistro del padre
                if pred.leftSon == node_to_delete:
                    pred.leftSon = ls
                elif pred.rightSon == node_to_delete:
                    pred.rightSon = ls
                ls.father = pred
                node_to_delete.leftSon = None
        self.balDelete(node_to_delete)  #ribilancia

    def delete_if_twoSon(self, node):
        node_to_delete = node
        #cerca il predecessore
        pred = self.find_pred(node_to_delete)
        pred.info, node_to_delete.info = node_to_delete.info, pred.info
        #verifica se il nodo nella nuova posizione ha uno o zero figli
        if pred.rightSon == None and pred.leftSon == None:
            self.delete_if_noSon(pred)
        elif pred.rightSon == None or pred.leftSon == None:
            self.delete_if_oneSon(pred)