Introducció als Arbres AVL
Els arbres AVL són un tipus especial d'arbre binari de cerca (BST) que es mantenen equilibrats automàticament. El nom AVL prové dels seus inventors, Adelson-Velsky i Landis, que els van introduir el 1962. La característica clau dels arbres AVL és que per a qualsevol node de l'arbre, la diferència de profunditat entre els seus subarbres esquerre i dret (anomenada factor d'equilibri) és com a màxim 1. Això garanteix que les operacions de cerca, inserció i eliminació es puguin realitzar en temps logarítmic.
Característiques dels Arbres AVL
- Equilibri: La diferència de profunditat entre els subarbres esquerre i dret de qualsevol node és com a màxim 1.
- Temps d'operació: Les operacions de cerca, inserció i eliminació es realitzen en O(log n) en el pitjor dels casos.
- Rotacions: Per mantenir l'equilibri, els arbres AVL utilitzen rotacions simples i dobles.
Rotacions en Arbres AVL
Quan es realitza una inserció o eliminació que desequilibra l'arbre, es necessiten rotacions per restaurar l'equilibri. Hi ha quatre tipus de rotacions:
- Rotació Simple a l'Esquerra (LL): Quan un node es desequilibra a l'esquerra a causa d'una inserció en el subarbre esquerre del seu fill esquerre.
- Rotació Simple a la Dreta (RR): Quan un node es desequilibra a la dreta a causa d'una inserció en el subarbre dret del seu fill dret.
- Rotació Doble a l'Esquerra-Dreta (LR): Quan un node es desequilibra a l'esquerra a causa d'una inserció en el subarbre dret del seu fill esquerre.
- Rotació Doble a la Dreta-Esquerra (RL): Quan un node es desequilibra a la dreta a causa d'una inserció en el subarbre esquerre del seu fill dret.
Exemple de Rotació Simple a la Dreta (RR)
Considerem un arbre AVL desequilibrat després d'una inserció:
Després d'una rotació simple a la dreta, l'arbre es converteix en:
Exemple de Rotació Doble a l'Esquerra-Dreta (LR)
Considerem un arbre AVL desequilibrat després d'una inserció:
Després d'una rotació doble a l'esquerra-dreta, l'arbre es converteix en:
Implementació d'un Arbre AVL en Python
A continuació es mostra una implementació bàsica d'un arbre AVL en Python:
class Node: def __init__(self, key): self.key = key self.left = None self.right = None self.height = 1 class AVLTree: def insert(self, root, key): if not root: return Node(key) elif key < root.key: root.left = self.insert(root.left, key) else: root.right = self.insert(root.right, key) root.height = 1 + max(self.get_height(root.left), self.get_height(root.right)) balance = self.get_balance(root) if balance > 1 and key < root.left.key: return self.right_rotate(root) if balance < -1 and key > root.right.key: return self.left_rotate(root) if balance > 1 and key > root.left.key: root.left = self.left_rotate(root.left) return self.right_rotate(root) if balance < -1 and key < root.right.key: root.right = self.right_rotate(root.right) return self.left_rotate(root) return root def left_rotate(self, z): y = z.right T2 = y.left y.left = z z.right = T2 z.height = 1 + max(self.get_height(z.left), self.get_height(z.right)) y.height = 1 + max(self.get_height(y.left), self.get_height(y.right)) return y def right_rotate(self, z): y = z.left T3 = y.right y.right = z z.left = T3 z.height = 1 + max(self.get_height(z.left), self.get_height(z.right)) y.height = 1 + max(self.get_height(y.left), self.get_height(y.right)) return y def get_height(self, root): if not root: return 0 return root.height def get_balance(self, root): if not root: return 0 return self.get_height(root.left) - self.get_height(root.right) def pre_order(self, root): if not root: return print("{0} ".format(root.key), end="") self.pre_order(root.left) self.pre_order(root.right) # Exemple d'ús avl = AVLTree() root = None keys = [10, 20, 30, 40, 50, 25] for key in keys: root = avl.insert(root, key) print("Preordre de l'arbre AVL:") avl.pre_order(root)
Explicació del Codi
- Node: La classe
Node
defineix un node de l'arbre AVL amb una clau, referències als fills esquerre i dret, i la seva alçada. - AVLTree: La classe
AVLTree
conté mètodes per inserir nodes, realitzar rotacions, obtenir l'alçada i el factor d'equilibri dels nodes, i imprimir l'arbre en preordre. - Inserció: El mètode
insert
insereix un nou node a l'arbre i ajusta les alçades i els equilibris mitjançant rotacions si és necessari. - Rotacions: Els mètodes
left_rotate
iright_rotate
realitzen les rotacions necessàries per mantenir l'arbre equilibrat. - Preordre: El mètode
pre_order
imprimeix l'arbre en preordre per verificar la seva estructura.
Exercici Pràctic
Exercici
Implementa una funció per eliminar un node d'un arbre AVL i mantenir l'equilibri de l'arbre.
Solució
class AVLTree: # ... (altres mètodes) def delete(self, root, key): if not root: return root if key < root.key: root.left = self.delete(root.left, key) elif key > root.key: root.right = self.delete(root.right, key) else: if root.left is None: temp = root.right root = None return temp elif root.right is None: temp = root.left root = None return temp temp = self.get_min_value_node(root.right) root.key = temp.key root.right = self.delete(root.right, temp.key) if root is None: return root root.height = 1 + max(self.get_height(root.left), self.get_height(root.right)) balance = self.get_balance(root) if balance > 1 and self.get_balance(root.left) >= 0: return self.right_rotate(root) if balance > 1 and self.get_balance(root.left) < 0: root.left = self.left_rotate(root.left) return self.right_rotate(root) if balance < -1 and self.get_balance(root.right) <= 0: return self.left_rotate(root) if balance < -1 and self.get_balance(root.right) > 0: root.right = self.right_rotate(root.right) return self.left_rotate(root) return root def get_min_value_node(self, root): if root is None or root.left is None: return root return self.get_min_value_node(root.left) # Exemple d'ús avl = AVLTree() root = None keys = [10, 20, 30, 40, 50, 25] for key in keys: root = avl.insert(root, key) print("Preordre de l'arbre AVL abans de l'eliminació:") avl.pre_order(root) root = avl.delete(root, 40) print("\nPreordre de l'arbre AVL després de l'eliminació:") avl.pre_order(root)
Explicació del Codi
- Eliminació: El mètode
delete
elimina un node de l'arbre AVL i ajusta les alçades i els equilibris mitjançant rotacions si és necessari. - Node amb Valor Mínim: El mètode
get_min_value_node
troba el node amb el valor mínim en un subarbre, utilitzat per substituir el node eliminat.
Conclusió
Els arbres AVL són una estructura de dades poderosa que garanteix operacions eficients en temps logarítmic mantenint l'equilibri de l'arbre. Comprendre les rotacions i la implementació d'arbres AVL és essencial per a qualsevol programador que treballi amb estructures de dades avançades.
Curs d'Estructures de Dades
Mòdul 1: Introducció a les Estructures de Dades
- Què són les Estructures de Dades?
- Importància de les Estructures de Dades en la Programació
- Tipus d'Estructures de Dades
Mòdul 2: Llistes
- Introducció a les Llistes
- Llistes Enllaçades
- Llistes Doblement Enllaçades
- Llistes Circulars
- Exercicis amb Llistes
Mòdul 3: Piles
- Introducció a les Piles
- Operacions Bàsiques amb Piles
- Implementació de Piles
- Aplicacions de les Piles
- Exercicis amb Piles
Mòdul 4: Cues
- Introducció a les Cues
- Operacions Bàsiques amb Cues
- Cues Circulars
- Cues de Prioritat
- Exercicis amb Cues
Mòdul 5: Arbres
- Introducció als Arbres
- Arbres Binàries
- Arbres Binàries de Cerca
- Arbres AVL
- Arbres B
- Exercicis amb Arbres
Mòdul 6: Grafs
- Introducció als Grafs
- Representació de Grafs
- Algoritmes de Cerca en Grafs
- Algoritmes de Camins Mínims
- Aplicacions dels Grafs
- Exercicis amb Grafs