import logging logger = logging.getLogger(__name__) # logging.basicConfig(level=logging.DEBUG) import time def timeMS(func, *args, **kwargs): startTime = time.perf_counter() result = func(*args, **kwargs) endTime = time.perf_counter() elapsedMS = (endTime - startTime) * 1000 # Convert to milliseconds print(f"{func.__name__} took {elapsedMS:.2f} ms") return result from utils.memory_array import MemoryArray from utils.literal import Literal from utils.memory_manager import MemoryManager from vorlesung.L05_binaere_baeume.bin_tree import BinaryTree from vorlesung.L05_binaere_baeume.bin_tree_node import BinaryTreeNode def analyze_complexity(fn, sizes): """ Analysiert die Komplexität einer maximalen Teilfolgenfunktion. :param max_sequence_func: Die Funktion, die analysiert wird. :param sizes: Eine Liste von Eingabegrößen für die Analyse. """ for size in sizes: MemoryManager.purge() # Speicher zurücksetzen random_array = MemoryArray.create_random_array(size, -100, 100) fn(random_array, Literal(0), random_array.length().pred()) MemoryManager.save_stats(size) MemoryManager.plot_stats(["cells", "adds", "compares", "reads", "writes"]) lineAccumulator = [] # Returnvalue does not get forwarded so we can not work with return. # Will try glob vars to append the string # Signature: def print_node(node, indent=0, line=None): def clbk_graphvizify(toDecorate : BinaryTreeNode, indent=0, line=None): global lineAccumulator if isinstance(toDecorate, AVLTreeNode): lineAccumulator.append(f'n_{id(toDecorate)} [label=<{toDecorate.value}
B: {toDecorate.balanceFactor}>]') else: lineAccumulator.append(f"n_{id(toDecorate)} [label={toDecorate.value}]") # Create edges for nodes with Child (use l - r) if toDecorate.left is not None: lineAccumulator.append(f"n_{id(toDecorate)} -> n_{id(toDecorate.left)}") if toDecorate.right is not None: lineAccumulator.append(f"n_{id(toDecorate)} -> n_{id(toDecorate.right)}") def graphvizify() -> str: # Header result = "digraph {\n\t" # Body result += ('\n\t'.join(str(item) for item in lineAccumulator)) # Footer result += "\n}" return result class AVLTreeNode(BinaryTreeNode): def __init__(self, value): super().__init__(value) self.parentRef = None # Start balanced as we probably have no children right after insert self.balanceFactor = Literal(0) def rightRotate(self, node) -> 'AVLTreeNode|None': if node is None: return None oLeft = node.left; oLeft.parentRef = node.parentRef; node.left = oLeft.right; if node.left is not None: node.left.parentRef = node; oLeft.right = node; node.parentRef = oLeft; if oLeft.parentRef is not None: if oLeft.parentRef.right is node: oLeft.parentRef.right = oLeft; elif oLeft.parentRef.left is node: oLeft.parentRef.left = oLeft; node.getSetBalanceFactor() oLeft.getSetBalanceFactor() return oLeft def leftRotate(self, node) -> 'AVLTreeNode|None': if node is None: return None oRight = node.right oRight.parentRef = node.parentRef node.right = oRight.left if node.right is not None: node.right.parentRef = node oRight.left = node node.parentRef = oRight if oRight.parentRef is not None: if oRight.parentRef.right is node: oRight.parentRef.right = oRight elif oRight.parentRef.left is node: oRight.parentRef.left = oRight node.getSetBalanceFactor() oRight.getSetBalanceFactor() return oRight def getSetBalanceFactor(self) -> Literal: leftHeight = self.left.height() if self.left else 0 rightHeight = self.right.height() if self.right else 0 self.balanceFactor = Literal(rightHeight - leftHeight) return self.balanceFactor def rightLeftRotate(self, node) -> 'AVLTreeNode|None': node.right = self.rightRotate(node.right) return self.leftRotate(node) def leftRightRotate(self, node) -> 'AVLTreeNode|None': node.left = self.leftRotate(node.left) return self.rightRotate(node) def debugTraverse(node, source=1): if node is None: return None logger.debug(f"{node.value} {node.getSetBalanceFactor()} {source}") debugTraverse(node.left, 10); debugTraverse(node.right, 20); class AVLTree(BinaryTree): # @override def new_node(self, value) -> AVLTreeNode: return AVLTreeNode(value) def balanceAVLTree(self, node : AVLTreeNode): # balance < -1 means imbalance to the left, > 1 means imbalance to the right logger.debug("in") if node is None: return None logger.debug("out") node.getSetBalanceFactor() logger.debug(f"Parent Balancing for {node.value} -> {node.balanceFactor} {node.left.height() if node.left else None} and {node.right.height() if node.right else None}") # imbalance to left -> If we enter this we cannot LOGICALLY have a left=None node -> No need to chekc if node.balanceFactor < Literal(-1): # Left-Left if node.left.balanceFactor <= Literal(0): # type: ignore -> Ignoring pywright error, see comment above # Wow, this syntax is sketchy ^^ # TODO Maybe declare as static if python supports this? Or just leaf param be? logger.debug("rr") node = node.rightRotate(node) # Left-Right else: # TODO Maybe declare as static if python supports this? Or just leaf param be? logger.debug("lrr") node = node.leftRightRotate(node) # Right heavy # imbalance to right -> If we enter this we cannot LOGICALLY have a right=None node -> No need to chekc if node.balanceFactor > Literal(1): # Right-Right case if node.right.balanceFactor >= Literal(0): # type: ignore -> Ignoring pywright error, see comment above # TODO Maybe declare as static if python supports this? Or just leaf param be? logger.debug("lr") node = node.leftRotate(node) # Right-Left case else: # TODO Maybe declare as static if python supports this? Or just leaf param be? logger.debug("rlr") node = node.rightLeftRotate(node) logger.debug(f"Reached {node.parentRef}") if node.parentRef is not None: logger.debug(f"Calling again for {node.parentRef.value}"); self.balanceAVLTree(node.parentRef); else: self.root = node; # Node is balanced return node # @override def insert(self, value): node, parent = super().insert(value) # NOTE Python does not have a Problem with NOT tellin us that we override something important # or something that does not exist.... This Makes for AWESOME debugging .... ... ... node.parentRef = parent if parent: node = self.balanceAVLTree(node.parentRef) return node, parent if __name__ == '__main__': tree = AVLTree() ### Force RR # testData = [30, 20, 10]; # for value in testData: # tree.insert(MemoryCell(value)); ### Force LR # testData = [10, 20, 30]; # for value in testData: # tree.insert(MemoryCell(value)); ### Force LRR # testData = [30, 10, 20] # for value in testData: # tree.insert(MemoryCell(value)) ### Force RLR # testData = [10, 30, 20] # for value in testData: # tree.insert(MemoryCell(value)) # Force rebuild of our balanceFactor indices... # debugTraverse(tree.root) binTreeData = MemoryArray.create_array_from_file("data/seq0.txt") for value in binTreeData: tree.insert(value) lineAccumulator.clear(); tree.in_order_traversal(clbk_graphvizify) # tree.tree_structure_traversal(clbk_graphvizify) print(graphvizify())