diff --git a/README.md b/README.md index 8c1b6ec..c002209 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,55 @@ -# tree-sort +# Tree sort Repository for the tree sort algorithm workshop. -You'll find the workshop material in the workshop folder. +You'll find the workshop material in the `workshop` folder. -## workshop/treesort.py +## Recipes + +Recipes used during the workshop to execute the tree sort algorithm manually (with a group of people). + +### To insert a node within a tree + +*This recipe inserts a new node into an (empty) tree. It is assumed there is a place defined where the tree will be started (rooting point?). When the recipe is executed the **current position** is considered to be rooting point.* + +1. Look at your *current position*, if it's empty (there is no node) this will be your place: you are inserted in the tree, and the recipe is finished. If there is a node continue with step two. + +2. Ask the node in the *current position* for its value. + +3. Compare the answer of the node to your own value. Is your value smaller than theirs? Then follow the left arm of the node, and take that as the new *current position* and move back to step one. Otherwise continue with step four. + +4. Is your own value the same as or bigger than the answer the node gave? Then follow the right arm of the node, and take that as your new *current position* and go back to step one. + + +### To measure the depth of a tree + +*This recipe discovers the depth of the deepest branch in the (sub)tree. It is initiated by asking the root or any other node to measure its depth.* + +1. Ask the node attached to your left arm for its depth, or take zero if you have none. + +2. Ask the node attached to your right arm for its depth, or take zero if you have none. + +3. Pick the greater one out of the answers you collected in step one and two, and add 1 (increment by one). + +4. Answer the number you calculated in step 3 to the node which asked you to measure your depth. + + +### To traverse the tree in order: + +*This recipe transforms the (sub)tree into an orderd row (or list)*. It is initiated by asking the root or any other node to traverse. + +1. If you have a left node: ask it to traverse. Once it has finished traversing ask the resulting row (list) to stand to the *left* of you. It is important the row maintains it order. + +2. If you have a right node: ask it to traverse. Once it has finished traversing ask the resulting row (list) to stand to the *right* of you. It is important the row maintains it order. + +3. By combining the row on the left, yourself, and the row to your right a new row has formed. Tell the node who asked you to traverse that you're ready. + + +## Code implementations + +In the folder `workshop/code` a simple implementation of the tree sort algorithm can be found. + +### workshop/code/treesort.py Implementation of the binary search tree. When the script is @@ -12,9 +57,17 @@ Implementation of the binary search tree. When the script is python workshop/treesort.py ``` -## workshop/text-tree.py +### workshop/code/tree-visualizer.py + +Visualizes a tree using graphviz + +``` +python/tree_visualizer.py +``` + +### workshop/code/text-tree.py -Reads a text (`workshop/text-to-sort.txt`)and generates a visual tree: +Reads a text (`workshop/text-to-sort.txt`) and generates a visual tree: ``` python/text-tree.py diff --git a/workshop/code/text-tree-utils.py b/workshop/code/text-tree-utils.py deleted file mode 100644 index 9c74286..0000000 --- a/workshop/code/text-tree-utils.py +++ /dev/null @@ -1,20 +0,0 @@ -from string import ascii_uppercase - -# Returns a function to generate node names with given prefix -def make_name_generator (prefix = '', length = 1): - wheels = [{ 'position': None, 'max': len(ascii_uppercase), 'values': list(ascii_uppercase)} for _ in range(length)] - - def name_generator (): - for wheel in wheels: - if wheel['position'] is None: - wheel['position'] = 0 - else: - wheel['position'] += 1 - if wheel['position'] < wheel['max']: - break - else: - wheel['position'] = 0 - - return prefix + ''.join(reversed([wheel['values'][wheel['position']] for wheel in wheels])) - - return name_generator diff --git a/workshop/code/text-tree.py b/workshop/code/text_tree.py similarity index 92% rename from workshop/code/text-tree.py rename to workshop/code/text_tree.py index 7485ee8..5e3f63d 100644 --- a/workshop/code/text-tree.py +++ b/workshop/code/text_tree.py @@ -1,5 +1,5 @@ from treesort import TreeNode -from visualizer import visualize +from tree_visualizer import visualize from random import shuffle import os.path diff --git a/workshop/code/tree_visualizer.py b/workshop/code/tree_visualizer.py new file mode 100644 index 0000000..3783c5a --- /dev/null +++ b/workshop/code/tree_visualizer.py @@ -0,0 +1,56 @@ +from string import ascii_uppercase +from graphviz import Digraph, Graph + +# Returns a function to generate node names with an optional prefix +def make_name_generator (length = 1, prefix = ''): + wheels = [{ 'position': None, 'max': len(ascii_uppercase), 'values': list(ascii_uppercase)} for _ in range(length)] + + def name_generator (): + for wheel in wheels: + if wheel['position'] is None: + wheel['position'] = 0 + else: + wheel['position'] += 1 + if wheel['position'] < wheel['max']: + break + else: + wheel['position'] = 0 + + return prefix + ''.join(reversed([wheel['values'][wheel['position']] for wheel in wheels])) + + return name_generator + +def visualize (tree, graphname): + generate_node_name = make_name_generator(length=3) + + def visualize_node (graph, node, previous_node_name = None, tailport = None): + if node: + node_name = generate_node_name() + graph.node(node_name, label=str(node.value)) + + if previous_node_name: + graph.edge(previous_node_name, node_name, tailport=tailport, headport='s') + + + visualize_node(graph, node.left, node_name, 'nw') + # Hack to have a more beautiful layout + visualize_node(graph, None, node_name, 'n') + visualize_node(graph, node.right, node_name, 'ne') + + else: + node_name = generate_node_name() + graph.node(node_name, label=node_name, style='invis') + graph.edge(previous_node_name, node_name, style='invis', tailport=tailport, headport='s') + + graph = Graph(name=graphname, format='svg', engine='dot') + graph.attr('graph', splines='line', rankdir='BT') + visualize_node(graph, tree) + graph.render(graphname) + +if __name__ == '__main__': + from treesort import make_tree + from random import randint + + values = [randint(0, 250) for _ in range(15)] + tree = make_tree(values) + visualize(tree, 'tree') \ No newline at end of file diff --git a/workshop/code/treesort.py b/workshop/code/treesort.py index c20aac8..a639f4c 100644 --- a/workshop/code/treesort.py +++ b/workshop/code/treesort.py @@ -23,7 +23,7 @@ def make_tree (values): """ Adds a node to the tree """ -def insert (root, value, key = lambda value: value): +def insert (root, value): # Test whether there is a node at the current location if not root: # If not, insert the node here @@ -34,10 +34,10 @@ def insert (root, value, key = lambda value: value): # There is a node, compare value to be inserted # and the value of the node to decide whether the # node should be attached to the left or the right - if key(value) < key(root.value): - root.left = insert(root.left, value, key) + if value < root.value: + root.left = insert(root.left, value) else: - root.right = insert(root.right, value, key) + root.right = insert(root.right, value) return root """ diff --git a/workshop/code/visualizer.py b/workshop/code/visualizer.py deleted file mode 100644 index 5fe9535..0000000 --- a/workshop/code/visualizer.py +++ /dev/null @@ -1,38 +0,0 @@ -from graph_utils import make_name_generator -from graphviz import Digraph, Graph - -generate_node_name = make_name_generator(length=3) - -def visualize_node (graph, node, previous_node_name = None, tailport = None): - if node: - node_name = generate_node_name() - graph.node(node_name, label=str(node.value)) - - if previous_node_name: - graph.edge(previous_node_name, node_name, tailport=tailport, headport='s') - - - visualize_node(graph, node.left, node_name, 'nw') - # Hack to have a more beautiful layout - visualize_node(graph, None, node_name, 'n') - visualize_node(graph, node.right, node_name, 'ne') - - else: - node_name = generate_node_name() - graph.node(node_name, label=node_name, style='invis') - graph.edge(previous_node_name, node_name, style='invis', tailport=tailport, headport='s') - - -def visualize (tree, graphname): - graph = Graph(name=graphname, format='svg', engine='dot') - graph.attr('graph', splines='line', rankdir='BT') - visualize_node (graph, tree) - graph.render(graphname) - -if __name__ == '__main__': - from treesort import make_tree - from random import randint - - values = [randint(0, 250) for _ in range(15)] - tree = make_tree(values) - visualize(tree, 'tree') \ No newline at end of file