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
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)
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)