From ac7068d26ca76599a3641b46707beca18013e559 Mon Sep 17 00:00:00 2001 From: Oliver Hofmann Date: Tue, 29 Apr 2025 18:19:35 +0200 Subject: [PATCH] AVL implementation --- praktika/05_avl_baum/gv_avl_tree.py | 12 +++ praktika/05_avl_baum/gv_binary_tree.py | 12 +++ .../L05_binaere_baeume/analyze_avl_tree.py | 26 ++++++ vorlesung/L05_binaere_baeume/avl_tree.py | 88 +++++++++++++++++++ vorlesung/L05_binaere_baeume/avl_tree_node.py | 62 +++++++++++++ 5 files changed, 200 insertions(+) create mode 100644 praktika/05_avl_baum/gv_avl_tree.py create mode 100644 praktika/05_avl_baum/gv_binary_tree.py create mode 100644 vorlesung/L05_binaere_baeume/analyze_avl_tree.py create mode 100644 vorlesung/L05_binaere_baeume/avl_tree.py create mode 100644 vorlesung/L05_binaere_baeume/avl_tree_node.py diff --git a/praktika/05_avl_baum/gv_avl_tree.py b/praktika/05_avl_baum/gv_avl_tree.py new file mode 100644 index 0000000..2b66847 --- /dev/null +++ b/praktika/05_avl_baum/gv_avl_tree.py @@ -0,0 +1,12 @@ +from utils.memory_array import MemoryArray +from vorlesung.L05_binaere_baeume.avl_tree import AVLTree + +if __name__ == "__main__": + a = MemoryArray.create_array_from_file("data/seq0.txt") + tree = AVLTree() + + for cell in a: + tree.insert(int(cell)) + + tree.graph_traversal() + diff --git a/praktika/05_avl_baum/gv_binary_tree.py b/praktika/05_avl_baum/gv_binary_tree.py new file mode 100644 index 0000000..b0afb58 --- /dev/null +++ b/praktika/05_avl_baum/gv_binary_tree.py @@ -0,0 +1,12 @@ +from utils.memory_array import MemoryArray +from vorlesung.L05_binaere_baeume.bin_tree import BinaryTree + +if __name__ == "__main__": + a = MemoryArray.create_array_from_file("data/seq0.txt") + tree = BinaryTree() + + for cell in a: + tree.insert(int(cell)) + + tree.graph_traversal() + diff --git a/vorlesung/L05_binaere_baeume/analyze_avl_tree.py b/vorlesung/L05_binaere_baeume/analyze_avl_tree.py new file mode 100644 index 0000000..96b74ca --- /dev/null +++ b/vorlesung/L05_binaere_baeume/analyze_avl_tree.py @@ -0,0 +1,26 @@ +from utils.memory_manager import MemoryManager +from utils.memory_array import MemoryArray +from utils.literal import Literal +from vorlesung.L05_binaere_baeume.avl_tree import AVLTree + +def analyze_complexity(sizes): + """ + Analysiert die Komplexität + + :param sizes: Eine Liste von Eingabegrößen für die Analyse. + """ + for size in sizes: + MemoryManager.purge() # Speicher zurücksetzen + tree = AVLTree() + random_array = MemoryArray.create_random_array(size, -100, 100) + for i in range(size-1): + tree.insert(int(random_array[Literal(i)])) + MemoryManager.reset() + tree.insert(int(random_array[Literal(size-1)])) + MemoryManager.save_stats(size) + + MemoryManager.plot_stats(["cells", "compares"]) + +if __name__ == "__main__": + sizes = range(1, 1001, 2) + analyze_complexity(sizes) \ No newline at end of file diff --git a/vorlesung/L05_binaere_baeume/avl_tree.py b/vorlesung/L05_binaere_baeume/avl_tree.py new file mode 100644 index 0000000..4a594bd --- /dev/null +++ b/vorlesung/L05_binaere_baeume/avl_tree.py @@ -0,0 +1,88 @@ +from vorlesung.L05_binaere_baeume.avl_tree_node import AVLTreeNode +from vorlesung.L05_binaere_baeume.bin_tree import BinaryTree + + +class AVLTree(BinaryTree): + + def __init__(self): + super().__init__() + + def new_node(self, value): + return AVLTreeNode(value) + + + def balance(self, node: AVLTreeNode): + node.update_balance() + if node.balance == -2: + if node.left.balance <= 0: + node = node.right_rotate() + else: + node = node.left_right_rotate() + elif node.balance == 2: + if node.right.balance >= 0: + node = node.left_rotate() + else: + node = node.right_left_rotate() + if node.parent: + self.balance(node.parent) + else: + self.root = node + # self.check_circle(self.root) + + def insert(self, value): + node, parent = super().insert(value) + node.parent = parent + if parent: + self.balance(parent) + return node, parent + + def delete(self, value): + node, parent = super().delete(value) + if node: + node.parent = parent + if parent: + self.balance(parent) + + def check_circle(self, node: AVLTreeNode, check_nodes = []): + if node is None: + return + if id(node) in check_nodes: + print("Circle detected") + return + self.check_circle(node.left, check_nodes + [id(node)]) + self.check_circle(node.right, check_nodes + [id(node)]) + + +if __name__ == "__main__": + + def print_node(node, indent=0, level=0): + print((indent * 3) * " ", node.value) + + tree = AVLTree() + values = [5, 3, 7, 2, 4, 6, 5, 8] + + for value in values: + tree.insert(value) + + + print("In-order traversal:") + tree.in_order_traversal(print_node) + print("\nLevel-order traversal:") + tree.level_order_traversal(print_node) + print("\nTree structure traversal:") + tree.tree_structure_traversal(print_node) + print("\nGraph traversal:") + tree.graph_traversal() + + tree.insert(9) + tree.graph_traversal() + + print("\nDeleting 5:") + tree.delete(5) + + print("In-order traversal after deletion:") + tree.in_order_traversal(print_node) + print("\nLevel-order traversal after deletion:") + tree.level_order_traversal(print_node) + print("\nTree structure traversal after deletion:") + tree.tree_structure_traversal(print_node) diff --git a/vorlesung/L05_binaere_baeume/avl_tree_node.py b/vorlesung/L05_binaere_baeume/avl_tree_node.py new file mode 100644 index 0000000..13a365a --- /dev/null +++ b/vorlesung/L05_binaere_baeume/avl_tree_node.py @@ -0,0 +1,62 @@ +from vorlesung.L05_binaere_baeume.bin_tree_node import BinaryTreeNode + +class AVLTreeNode(BinaryTreeNode): + def __init__(self, value): + super().__init__(value) + self.parent = None + self.balance = 0 + + def __repr__(self): + return f"TreeNode(id={id(self)} value={self.value}, left={self.left}, right={self.right})" + + def gv_rep(self, row, col): + """Returns the graphviz representation of the node.""" + return f"{id(self)} [label=\"{self.value}\", pos=\"{col},{-row}!\", xlabel=\"{self.balance}\"];\n" + + def update_balance(self): + left_height = self.left.height() if self.left else 0 + right_height = self.right.height() if self.right else 0 + self.balance = right_height - left_height + + def right_rotate(self): + new_root = self.left + new_root.parent = self.parent + self.left = new_root.right + if self.left: + self.left.parent = self + new_root.right = self + self.parent = new_root + if new_root.parent: + if new_root.parent.left is self: + new_root.parent.left = new_root + else: + new_root.parent.right = new_root + self.update_balance() + new_root.update_balance() + return new_root + + def left_rotate(self): + new_root = self.right + new_root.parent = self.parent + self.right = new_root.left + if self.right: + self.right.parent = self + new_root.left = self + self.parent = new_root + if new_root.parent: + if new_root.parent.left is self: + new_root.parent.left = new_root + else: + new_root.parent.right = new_root + self.update_balance() + new_root.update_balance() + return new_root + + def right_left_rotate(self): + self.right = self.right.right_rotate() + return self.left_rotate() + + def left_right_rotate(self): + self.left = self.left.left_rotate() + return self.right_rotate() +