Oliver Hofmann 63a3460c21 switched to Graphviz module
Better filename
2025-05-03 11:26:40 +02:00

209 lines
6.9 KiB
Python

from vorlesung.L05_binaere_baeume.bin_tree_node import BinaryTreeNode
from utils.memory_manager import MemoryManager
from utils.memory_array import MemoryArray
from utils.project_dir import get_path
from datetime import datetime
import graphviz
class BinaryTree:
def __init__(self):
self.root = None
self.size = 0
def new_node(self, value):
return BinaryTreeNode(value)
def insert(self, value):
self.size += 1
value = self.new_node(value)
if self.root is None:
self.root = value
return self.root, None
else:
current = self.root
while True:
if value < current:
if current.left:
current = current.left
else:
current.left = value
return current.left, current
elif value >= current:
if current.right:
current = current.right
else:
current.right = value
return current.right, current
else:
return None, None
def search(self, value):
current = self.root
value = self.new_node(value)
while current:
if value < current:
current = current.left
elif value > current:
current = current.right
else:
return current
return None
def delete(self, value):
# Der Wert wird im Baum gesucht und der erste Treffer gelöscht
# Rückgabe falls der Wert gefunden wird:
# der Knoten, der den zu löschenden Knoten ersetzt und der Elternknoten des gelöschten Knotens
parent = None
current = self.root
value = self.new_node(value)
while current:
if value < current:
parent = current
current = current.left
elif value > current:
parent = current
current = current.right
else:
# Knoten gefunden
break
else:
# Wert nicht gefunden
return None, None
return self.delete_node(current, parent)
def delete_node(self, current, parent):
# Der übergebene Knoten wird
# Rückgabe ist ein Tupel:
# der Knoten, der den zu löschenden Knoten ersetzt und der Elternknoten des gelöschten Knotens
self.size -= 1
# Fall 3: Es gibt zwei Kinder: wir suchen den Nachfolger
if current.left and current.right:
parent = current
successor = current.right
while successor.left:
parent = successor
successor = successor.left
# Wert des Nachfolgers wird in den Knoten geschrieben, der gelöscht werden soll
current.value = successor.value
# Ab jetzt muss successor gelöscht werden; parent ist bereits richtig gesetzt
current = successor
# Ermitteln des einen Kindes (falls es eines gibt), sonst None
# Das eine Kind ist der Ersatz für den Knoten, der gelöscht werden soll
if current.left:
child = current.left
else:
child = current.right
# Falls es keinen Elternknoten gibt, ist der Ersatzknoten die Wurzel
if not parent:
self.root = child
return child, None
elif parent.left is current:
parent.left = child
return child, parent
else:
parent.right = child
return child, parent
def in_order_traversal(self, callback):
def in_order_traversal_recursive(callback, current):
if current is not None:
in_order_traversal_recursive(callback, current.left)
callback(current)
in_order_traversal_recursive(callback, current.right)
in_order_traversal_recursive(callback, self.root)
def level_order_traversal(self, callback):
if self.root is None:
return
queue = [(self.root, 0)]
while queue:
current, level = queue.pop(0)
callback(current, level)
if current.left is not None:
queue.append((current.left, level + 1))
if current.right is not None:
queue.append((current.right, level + 1))
def tree_structure_traversal(self, callback):
def tree_structure_traversal_recursive(callback, current, level):
nonlocal line
if current:
tree_structure_traversal_recursive(callback, current.left, level + 1)
callback(current, level, line)
line += 1
tree_structure_traversal_recursive(callback, current.right, level + 1)
line = 0
tree_structure_traversal_recursive(callback, self.root, 0)
def graph_filename(self):
return "BinaryTree"
def graph_traversal(self):
def define_node(node, level, line):
nonlocal dot
if node is not None:
node.graphviz_rep(level, line, dot)
def graph_traversal_recursive(current):
nonlocal dot
if current is not None:
if current.left:
dot.edge(str(id(current)), str(id(current.left)))
graph_traversal_recursive(current.left)
if current.right:
dot.edge(str(id(current)), str(id(current.right)))
graph_traversal_recursive(current.right)
dot = graphviz.Digraph( name="BinaryTree",
engine="neato",
node_attr={"shape": "circle", "fontname": "Arial"},
format="pdf" )
self.tree_structure_traversal(define_node)
graph_traversal_recursive(self.root)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"{self.graph_filename()}_{timestamp}.gv"
filename = get_path(filename)
dot.render(filename)
if __name__ == "__main__":
tree = BinaryTree()
values = [5, 3, 7, 2, 4, 6, 5, 8]
for value in values:
tree.insert(value)
def print_node(node, indent=0, line=None):
print((indent * 3) * " ", node.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()
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)