@@ -0,0 +1,111 @@ | |||
from SoSe24.algodat.foundation import AlgoDatArray, AlgoDatValue, read_int_sequence | |||
from SoSe24.lec04_trees.bin_tree import BinTree, BinTreeNode | |||
from time import perf_counter as pfc | |||
class AVLTreeNode(BinTreeNode): | |||
def __init__(self, value: AlgoDatValue): | |||
super().__init__(value) | |||
self.parent = None | |||
self.balance = 0 | |||
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) -> BinTreeNode: | |||
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 == 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) -> BinTreeNode: | |||
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 == 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) -> BinTreeNode: | |||
self.right = self.right.right_rotate() | |||
return self.left_rotate() | |||
def left_right_rotate(self) -> BinTreeNode: | |||
self.left = self.left.left_rotate() | |||
return self.right_rotate() | |||
class AVLTree(BinTree): | |||
def new_node(self, value: AlgoDatValue): | |||
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 | |||
def insert(self, value: AlgoDatValue): | |||
node, parent = super().insert(value) | |||
node.parent = parent | |||
if parent: | |||
self.balance(parent) | |||
return node, parent | |||
def delete(self, value: AlgoDatValue): | |||
node, parent = super().delete(value) | |||
if node: | |||
node.parent = parent | |||
if parent: | |||
self.balance(parent) | |||
if __name__ == "__main__": | |||
z = read_int_sequence("../../seq0.txt") | |||
print(z, len(z)) | |||
start = pfc() | |||
tree = AVLTree() | |||
for i in z: | |||
tree.insert(i) | |||
tree.walk() | |||
tree.tree_walk() | |||
tree.levelwalk() | |||
tree.graph_walk() | |||
tree.delete(AlgoDatValue(46)) | |||
tree.delete(AlgoDatValue(48)) | |||
#tree.graph_walk() | |||
print(f"Dauer: {pfc() - start:.4f}s") | |||
AlgoDatValue.summary() |
@@ -0,0 +1,29 @@ | |||
from SoSe24.algodat.foundation import AlgoDatArray, AlgoDatValue, read_int_sequence, read_int_sequence_limited | |||
import matplotlib | |||
matplotlib.use('TkAgg') | |||
import matplotlib.pyplot as plt | |||
import avl_tree as avl | |||
if __name__ == "__main__": | |||
filename = "../../seq3_sorted.txt" | |||
#filename = "../../seq3.txt" | |||
dummy = read_int_sequence(filename) | |||
n = len(dummy) | |||
step = n // 100 | |||
memory_values = [] | |||
compare_values = [] | |||
for right_end in range(1, n, step): | |||
AlgoDatValue.reset() | |||
z = read_int_sequence_limited(filename, right_end) | |||
tree = avl.AVLTree() | |||
for i in z: | |||
tree.insert(i) | |||
memory_values.append(AlgoDatValue.memory) | |||
compare_values.append(AlgoDatValue.compare) | |||
plt.plot(range(1, n, step), memory_values, 'b', label='Memory') | |||
plt.plot(range(1, n, step), compare_values, 'r', label='Compare') | |||
plt.legend() | |||
plt.show() |
@@ -0,0 +1,133 @@ | |||
from SoSe24.algodat.foundation import AlgoDatArray, AlgoDatValue, read_int_sequence | |||
from time import perf_counter as pfc | |||
class BTreeNode: | |||
def __init__(self, m: int): | |||
self.n = 0 | |||
self.leaf = True | |||
self.keys = AlgoDatArray(2 * m - 1) | |||
self.children = [None] * (2 * m) | |||
def __str__(self): | |||
return "(" + " ".join([str(self.keys[i]) for i in range(self.n)]) + ")" | |||
class BTree: | |||
def __init__(self, m: int): | |||
self.m = m | |||
self.root = BTreeNode(m) | |||
def search(self, key: AlgoDatValue, start: BTreeNode = None) -> BTreeNode: | |||
if not start: | |||
start = self.root | |||
i = 0 | |||
while i < start.n and key > start.keys[i]: | |||
i += 1 | |||
if i < start.n and key == start.keys[i]: | |||
return start | |||
if start.leaf: | |||
return None | |||
return self.search(key, start.children[i]) | |||
def split_child(self, parent: BTreeNode, i: int): | |||
child = parent.children[i] | |||
h = BTreeNode(self.m) | |||
h.leaf = child.leaf | |||
h.n = self.m - 1 | |||
for j in range(self.m - 1): | |||
h.keys[j] = child.keys[j + self.m] | |||
if not h.leaf: | |||
for j in range(self.m): | |||
h.children[j] = child.children[j + self.m] | |||
for j in range(self.m, child.n + 1): | |||
child.children[j] = None | |||
child.n = self.m - 1 | |||
for j in range(parent.n, i, -1): | |||
parent.children[j + 1] = parent.children[j] | |||
parent.keys[j] = parent.keys[j - 1] | |||
parent.children[i + 1] = h | |||
parent.keys[i] = child.keys[self.m - 1] | |||
parent.n += 1 | |||
def insert(self, k: AlgoDatValue): | |||
r = self.root | |||
if r.n == 2 * self.m - 1: | |||
h = BTreeNode(self.m) | |||
self.root = h | |||
h.leaf = False | |||
h.n = 0 | |||
h.children[0] = r | |||
self.split_child(h, 0) | |||
self.insert_in_node(h, k) | |||
else: | |||
self.insert_in_node(r, k) | |||
def insert_in_node(self, start: BTreeNode, k: AlgoDatValue): | |||
i = start.n | |||
if start.leaf: | |||
while i >= 1 and k < start.keys[i-1]: | |||
start.keys[i] = start.keys[i-1] | |||
i -= 1 | |||
start.keys[i] = k | |||
start.n += 1 | |||
else: | |||
j = 0 | |||
while j < start.n and k > start.keys[j]: | |||
j += 1 | |||
if start.children[j].n == 2 * self.m - 1: | |||
self.split_child(start, j) | |||
if k > start.keys[j]: | |||
j += 1 | |||
self.insert_in_node(start.children[j], k) | |||
def walk(self, start: BTreeNode = None): | |||
if not start: | |||
start = self.root | |||
i = 0 | |||
while i < start.n: | |||
if not start.leaf: | |||
self.walk(start.children[i]) | |||
print(start.keys[i], end=" ") | |||
i += 1 | |||
if not start.leaf: | |||
self.walk(start.children[i]) | |||
def height(self, start: BTreeNode = None): | |||
if not start: | |||
start = self.root | |||
if start.leaf: | |||
return 0 | |||
return 1 + self.height(start.children[0]) | |||
def graph_walk(self): | |||
queue = [ self.root ] | |||
with open("../../btree.gv", "w") as file: | |||
file.write("digraph BTree {\n") | |||
file.write(" node [fontname=\"Arial\"];\n") | |||
while queue: | |||
current = queue.pop(0) | |||
p = str(current) | |||
file.write(f'"{p}"; \n') | |||
i = 0 | |||
while i <= current.n: | |||
if not current.leaf: | |||
queue.append(current.children[i]) | |||
c = str(current.children[i]) | |||
file.write(f'"{p}" -> "{c}";\n') | |||
i += 1 | |||
file.write("}") | |||
if __name__ == "__main__": | |||
z = read_int_sequence("../../seq2.txt") | |||
start = pfc() | |||
tree = BTree(3) | |||
for i in z: | |||
tree.insert(i) | |||
print(f"Height: {tree.height()}") | |||
tree.walk() | |||
tree.graph_walk() | |||
s = tree.search(AlgoDatValue(0)) | |||
print(f"\nKnoten mit 0: {str(s)}") | |||
print(f"Dauer: {pfc() - start:.4f}s") | |||
AlgoDatValue.summary() |
@@ -0,0 +1,171 @@ | |||
from SoSe24.algodat.foundation import AlgoDatArray, AlgoDatValue, read_int_sequence | |||
from time import perf_counter as pfc | |||
class BinTreeNode: | |||
def __init__(self, value: AlgoDatValue): | |||
self.value = value | |||
self.left = None | |||
self.right = None | |||
def __str__(self): | |||
return f"{self.value}" | |||
def height(self) -> int: | |||
left_height = self.left.height() if self.left else 0 | |||
right_height = self.right.height() if self.right else 0 | |||
return 1 + max(left_height, right_height) | |||
class BinTree: | |||
def __init__(self): | |||
self.root = None | |||
def new_node(self, value: AlgoDatValue) -> BinTreeNode: | |||
return BinTreeNode(value) | |||
def insert(self, value: AlgoDatValue) -> (BinTreeNode, BinTreeNode): | |||
if not self.root: | |||
self.root = self.new_node(value) | |||
return self.root, None | |||
current = self.root | |||
while True: | |||
if value < current.value: | |||
if current.left: | |||
current = current.left | |||
else: | |||
current.left = self.new_node(value) | |||
return current.left, current | |||
elif value >= current.value: | |||
if current.right: | |||
current = current.right | |||
else: | |||
current.right = self.new_node(value) | |||
return current.right, current | |||
else: | |||
return None, None | |||
def search(self, value: AlgoDatValue) -> BinTreeNode: | |||
current = self.root | |||
while current: | |||
if value < current.value: | |||
current = current.left | |||
elif value > current.value: | |||
current = current.right | |||
else: | |||
return current | |||
return None | |||
def delete(self, value: AlgoDatValue): | |||
parent = None | |||
current = self.root | |||
while current: | |||
if value < current.value: | |||
parent = current | |||
current = current.left | |||
elif value >= current.value: | |||
parent = current | |||
current = current.right | |||
else: | |||
break | |||
else: | |||
return | |||
if current.left and current.right: | |||
parent = current | |||
successor = current.right | |||
while successor.left: | |||
parent = successor | |||
successor = successor.left | |||
current.value.value = successor.value.value | |||
current = successor | |||
if current.left: | |||
child = current.left | |||
else: | |||
child = current.right | |||
if not parent: | |||
self.root = child | |||
return child, None | |||
elif parent.left == current: | |||
parent.left = child | |||
return child, parent | |||
else: | |||
parent.right = child | |||
return child, parent | |||
def walk(self): # in-order | |||
print("[ ", end="") | |||
self.walk_recursive(self.root, 0, 0) | |||
print(" ]") | |||
def graph_walk(self): | |||
self.leaf_counter = 0 | |||
with open("../../graph.gv", "w") as file: | |||
file.write("digraph BST {\n") | |||
file.write(" node [fontname=\"Arial\"];\n") | |||
self.graph_walk_recursive(self.root, file) | |||
file.write("}") | |||
def graph_walk_recursive(self, current, file): | |||
if current is not None: | |||
if current.left: | |||
file.write(f"{current.value} -> {current.left.value}; \n") | |||
self.graph_walk_recursive(current.left, file) | |||
else: | |||
file.write(f"left{self.leaf_counter} [shape=point]; \n") | |||
file.write(f"{current.value} -> left{self.leaf_counter}; \n") | |||
self.leaf_counter += 1 | |||
if current.right: | |||
file.write(f"{current.value} -> {current.right.value}; \n") | |||
self.graph_walk_recursive(current.right, file) | |||
else: | |||
file.write(f"right{self.leaf_counter} [shape=point]; \n") | |||
file.write(f"{current.value} -> right{self.leaf_counter}; \n") | |||
self.leaf_counter += 1 | |||
def tree_walk(self): | |||
self.walk_recursive(self.root, 0, 1) | |||
def walk_recursive(self, node: BinTreeNode, level = 0, increase = 1): | |||
if node: | |||
if increase >= 1: | |||
end = "\n" | |||
else: | |||
end = " " | |||
self.walk_recursive(node.left, level+increase, increase) | |||
print(" "*level*3 + str(node.value), end=end) | |||
self.walk_recursive(node.right, level+increase, increase) | |||
def levelwalk(self): | |||
if self.root is None: | |||
return | |||
queue = [self.root] | |||
while queue: | |||
current = queue.pop(0) | |||
print(current.value, end=" ") | |||
if current.left: | |||
queue.append(current.left) | |||
if current.right: | |||
queue.append(current.right) | |||
print() | |||
if __name__ == "__main__": | |||
z = read_int_sequence("../../seq0.txt") | |||
print(z, len(z)) | |||
start = pfc() | |||
tree = BinTree() | |||
for i in z: | |||
tree.insert(i) | |||
tree.walk() | |||
tree.tree_walk() | |||
tree.levelwalk() | |||
tree.delete(AlgoDatValue(46)) | |||
tree.graph_walk() | |||
print(f"Dauer: {pfc() - start:.4f}s") | |||
AlgoDatValue.summary() |
@@ -0,0 +1,30 @@ | |||
from SoSe24.algodat.foundation import AlgoDatArray, AlgoDatValue, read_int_sequence, read_int_sequence_limited | |||
import matplotlib | |||
matplotlib.use('TkAgg') | |||
import matplotlib.pyplot as plt | |||
import bin_tree as bt | |||
if __name__ == "__main__": | |||
filename = "../../seq3_sorted.txt" | |||
#filename = "../../seq3.txt" | |||
dummy = read_int_sequence(filename) | |||
n = len(dummy) | |||
step = n // 100 | |||
memory_values = [] | |||
compare_values = [] | |||
for right_end in range(1, n, step): | |||
AlgoDatValue.reset() | |||
z = read_int_sequence_limited(filename, right_end) | |||
tree = bt.BinTree() | |||
for i in z: | |||
tree.insert(i) | |||
memory_values.append(AlgoDatValue.memory) | |||
compare_values.append(AlgoDatValue.compare) | |||
print(right_end, AlgoDatValue.compare) | |||
plt.plot(range(1, n, step), memory_values, 'b', label='Memory') | |||
plt.plot(range(1, n, step), compare_values, 'r', label='Compare') | |||
plt.legend() | |||
plt.show() |
@@ -0,0 +1,91 @@ | |||
from SoSe24.algodat.foundation import AlgoDatArray, AlgoDatValue, read_int_sequence, MinusInf | |||
from time import perf_counter as pfc | |||
#Goldener Schnitt | |||
import math | |||
a = (math.sqrt(5) - 1) / 2 | |||
def h(x, m): | |||
return int(x*a - int(x*a) * m) | |||
def f(x, i, m): | |||
return (h(x, m) + i + 14*i*i) % m | |||
def f1(x, i, m): | |||
if i % 2 == 0: | |||
return (h(x, m) + i*i) % m | |||
return ((h(x, m) - i*i) % m + m) % m | |||
class HashTable: | |||
def __init__(self, m, h, f=None): | |||
self.m = m | |||
self.h = h | |||
self.f = f | |||
self.table = AlgoDatArray(m) | |||
def insert(self, x): | |||
i = 0 | |||
while i < self.m: | |||
j = self.f(x.value, i, self.m) | |||
if self.is_free(j): | |||
self.table[j].value = x.value | |||
return True | |||
i += 1 | |||
return False | |||
def search(self, x): | |||
i = 0 | |||
while i < self.m: | |||
j = f(x, i, self.m) | |||
if self.table[j] == x: | |||
return True | |||
if self.table[j] == None: | |||
return False | |||
i += 1 | |||
return False | |||
def delete(self, x): | |||
i = 0 | |||
while i < self.m: | |||
j = f(x, i, self.m) | |||
if self.table[j].value == x: | |||
self.table[j].value = "DELETED" | |||
return True | |||
if self.table[j].value is None: | |||
return False | |||
i += 1 | |||
return False | |||
def __str__(self): | |||
return str(self.table) | |||
def alpha(self): | |||
i=0 | |||
used = 0 | |||
while i < self.m: | |||
used += 0 if self.is_free(i) else 1 | |||
i += 1 | |||
return used / self.m | |||
def is_free(self, i): | |||
if self.table[i] == None: | |||
return True | |||
if self.table[i] == "DELETED": | |||
return True | |||
return False | |||
if __name__ == "__main__": | |||
z = read_int_sequence("../../seq1.txt") | |||
start = pfc() | |||
hash = HashTable(31, h, f) | |||
for i in z: | |||
hash.insert(i) | |||
print(hash) | |||
print(f"Alpha: {hash.alpha()}") | |||
hash.delete(34) | |||
hash.search(47) | |||
hash.search(243) | |||
print(hash) | |||
print(f"Alpha: {hash.alpha()}") | |||
print(f"Dauer: {pfc() - start:.4f}s") | |||
AlgoDatValue.summary() |
@@ -0,0 +1,181 @@ | |||
from collections import deque | |||
from typing import List | |||
import re | |||
from enum import Enum | |||
class NodeColor(Enum): | |||
"""Enumeration for node colors in a graph traversal.""" | |||
WHITE = 1 # WHITE: not visited | |||
GRAY = 2 # GRAY: visited but not all neighbors visited | |||
BLACK = 3 # BLACK: visited and all neighbors visited | |||
class Vertex: | |||
"""A vertex in a graph.""" | |||
def __init__(self, value): | |||
self.value = value | |||
class Graph: | |||
"""A graph.""" | |||
def insert_vertex(self, name: str): | |||
raise NotImplementedError("Please implement this method in subclass") | |||
def connect(self, name1: str, name2: str): | |||
raise NotImplementedError("Please implement this method in subclass") | |||
def all_vertices(self) -> List[Vertex]: | |||
raise NotImplementedError("Please implement this method in subclass") | |||
def get_vertex(self, name: str) -> Vertex: | |||
raise NotImplementedError("Please implement this method in subclass") | |||
def get_adjacent_vertices(self, name: str) -> List[Vertex]: | |||
raise NotImplementedError("Please implement this method in subclass") | |||
def bfs(self, start_name: str): | |||
""" | |||
Perform a breadth-first search starting at the given vertex. | |||
:param start_name: the name of the vertex to start at | |||
:return: a tuple of two dictionaries, the first mapping vertices to distances from the start vertex, | |||
the second mapping vertices to their predecessors in the traversal tree | |||
""" | |||
color_map = {} # maps vertices to their color | |||
distance_map = {} # maps vertices to their distance from the start vertex | |||
predecessor_map = {} # maps vertices to their predecessor in the traversal tree | |||
# Initialize the maps | |||
for vertex in self.all_vertices(): | |||
color_map[vertex] = NodeColor.WHITE | |||
distance_map[vertex] = None | |||
predecessor_map[vertex] = None | |||
# Start at the given vertex | |||
start_node = self.get_vertex(start_name) | |||
color_map[start_node] = NodeColor.GRAY | |||
distance_map[start_node] = 0 | |||
# Initialize the queue with the start vertex | |||
queue = deque() | |||
queue.append(start_node) | |||
# Process the queue | |||
while len(queue) > 0: | |||
vertex = queue.popleft() | |||
for dest in self.get_adjacent_vertices(vertex.value): | |||
if color_map[dest] == NodeColor.WHITE: | |||
color_map[dest] = NodeColor.GRAY | |||
distance_map[dest] = distance_map[vertex] + 1 | |||
predecessor_map[dest] = vertex | |||
queue.append(dest) | |||
color_map[vertex] = NodeColor.BLACK | |||
# Return the distance and predecessor maps | |||
return distance_map, predecessor_map | |||
def path(self, destination, map): | |||
""" | |||
Compute the path from the start vertex to the given destination vertex. | |||
The map parameter is the predecessor map | |||
""" | |||
path = [] | |||
destination_node = self.get_vertex(destination) | |||
while destination_node is not None: | |||
path.insert(0, destination_node.value) | |||
destination_node = map[destination_node] | |||
return path | |||
class AdjacencyListGraph(Graph): | |||
"""A graph implemented as an adjacency list.""" | |||
def __init__(self): | |||
self.adjacency_map = {} # maps vertex names to lists of adjacent vertices | |||
self.vertex_map = {} # maps vertex names to vertices | |||
def insert_vertex(self, name: str): | |||
if name not in self.vertex_map: | |||
self.vertex_map[name] = Vertex(name) | |||
if name not in self.adjacency_map: | |||
self.adjacency_map[name] = [] | |||
def connect(self, name1: str, name2: str): | |||
adjacency_list = self.adjacency_map[name1] | |||
dest = self.vertex_map[name2] | |||
adjacency_list.append(dest) | |||
def all_vertices(self) -> List[Vertex]: | |||
return list(self.vertex_map.values()) | |||
def get_vertex(self, name: str) -> Vertex: | |||
return self.vertex_map[name] | |||
def get_adjacent_vertices(self, name: str) -> List[Vertex]: | |||
return self.adjacency_map[name] | |||
class AdjacencyMatrixGraph(Graph): | |||
"""A graph implemented as an adjacency matrix.""" | |||
def __init__(self): | |||
self.index_map = {} # maps vertex names to indices | |||
self.vertex_list = [] # list of vertices | |||
self.adjacency_matrix = [] # adjacency matrix | |||
def insert_vertex(self, name: str): | |||
if name not in self.index_map: | |||
self.index_map[name] = len(self.vertex_list) | |||
self.vertex_list.append(Vertex(name)) | |||
for row in self.adjacency_matrix: # add a new column to each row | |||
row.append(0) | |||
self.adjacency_matrix.append([0] * len(self.vertex_list)) # add a new row | |||
def connect(self, name1: str, name2: str): | |||
index1 = self.index_map[name1] | |||
index2 = self.index_map[name2] | |||
self.adjacency_matrix[index1][index2] = 1 | |||
def all_vertices(self) -> List[Vertex]: | |||
return self.vertex_list | |||
def get_vertex(self, name: str) -> Vertex: | |||
index = self.index_map[name] | |||
return self.vertex_list[index] | |||
def get_adjacent_vertices(self, name: str) -> List[Vertex]: | |||
index = self.index_map[name] | |||
result = [] | |||
for i in range(len(self.vertex_list)): | |||
if self.adjacency_matrix[index][i] == 1: | |||
name = self.vertex_list[i].value | |||
result.append(self.get_vertex(name)) | |||
return result | |||
def read_cave_into_graph(graph: Graph, filename: str): | |||
"""Read a cave description from a file and insert it into the given graph.""" | |||
with open(filename, "r") as file: | |||
lines = file.readlines() | |||
for line in lines: | |||
# match a line with two node names and an optional direction | |||
m = re.match(r"(^\s*\"(.*)\"\s*([<>]*)\s*\"(.*)\"\s*)", line) | |||
if m: | |||
startnode = m.group(2) | |||
endnode = m.group(4) | |||
opcode = m.group(3) | |||
graph.insert_vertex(startnode) | |||
graph.insert_vertex(endnode) | |||
if '>' in opcode: | |||
graph.connect(startnode, endnode) | |||
if '<' in opcode: | |||
graph.connect(endnode, startnode) | |||
if __name__ == "__main__": | |||
graph = AdjacencyListGraph() | |||
#graph = AdjacencyMatrixGraph() | |||
read_cave_into_graph(graph, "../../hoehle.txt") | |||
_, predecessor_map = graph.bfs('Höhleneingang') | |||
path = graph.path('Schatzkammer', predecessor_map) | |||
print(path) | |||
_, predecessor_map = graph.bfs('Schatzkammer') | |||
path = graph.path('Höhleneingang', predecessor_map) | |||
print(path) |