diff --git a/schoeffelbe/pr05.py b/schoeffelbe/pr05.py index 4c8f201..84d539f 100644 --- a/schoeffelbe/pr05.py +++ b/schoeffelbe/pr05.py @@ -14,11 +14,8 @@ def timeMS(func, *args, **kwargs): from utils.memory_array import MemoryArray -from utils.memory_cell import MemoryCell from utils.literal import Literal -from utils.constants import MAX_VALUE from utils.memory_manager import MemoryManager -from utils.memory_range import mrange from vorlesung.L05_binaere_baeume.bin_tree import BinaryTree from vorlesung.L05_binaere_baeume.bin_tree_node import BinaryTreeNode @@ -39,12 +36,16 @@ def analyze_complexity(fn, sizes): lineAccumulator = [] -# its not getting forwarded so we can not work with return. I will try glob vars to append the string +# 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 - lineAccumulator.append(f"n_{id(toDecorate)} [label={toDecorate.value}]") + if isinstance(toDecorate, AVLTreeNode): + lineAccumulator.append(f"n_{id(toDecorate)} [label=\"{toDecorate.value}\\n{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: @@ -53,26 +54,193 @@ def clbk_graphvizify(toDecorate : BinaryTreeNode, indent=0, line=None): 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)) - # Foot + # 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 = BinaryTree() - values = [5, 3, 7, 2, 4, 6, 5, 8] + 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) - # for value in values: - # tree.insert(value) + lineAccumulator.clear(); tree.in_order_traversal(clbk_graphvizify) + # tree.tree_structure_traversal(clbk_graphvizify) print(graphvizify())