Code files
parent
d800575095
commit
5840d75449
@ -1,2 +1,21 @@
|
|||||||
# tree-sort
|
# tree-sort
|
||||||
|
|
||||||
|
Repository for the tree sort algorithm workshop.
|
||||||
|
|
||||||
|
You'll find the workshop material in the workshop folder.
|
||||||
|
|
||||||
|
## workshop/treesort.py
|
||||||
|
|
||||||
|
Implementation of the binary search tree. When the script is
|
||||||
|
|
||||||
|
```
|
||||||
|
python workshop/treesort.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## workshop/text-tree.py
|
||||||
|
|
||||||
|
Reads a text (`workshop/text-to-sort.txt`)and generates a visual tree:
|
||||||
|
|
||||||
|
```
|
||||||
|
python/text-tree.py
|
||||||
|
```
|
@ -0,0 +1,20 @@
|
|||||||
|
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
|
@ -0,0 +1 @@
|
|||||||
|
"the quality of light by which we scrutinize our lives has direct bearing upon the product which we live, and upon the changes which we hope to bring about through those lives. It is within this light that we form those ideas by which we pursue our magic and make it realized. This is poetry as illumination, for it is through poetry that we give name to those ideas which are - until the poem - nameless and formless, about to be birthed, but already felt. That distillation of experience from which true poetry springs births thought as dream births concept, as feeling births idea, as knowledge births (precedes) understanding."
|
@ -0,0 +1,48 @@
|
|||||||
|
from treesort import TreeNode
|
||||||
|
from visualizer import visualize
|
||||||
|
from random import shuffle
|
||||||
|
import os.path
|
||||||
|
|
||||||
|
basepath = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
# Alternative sorting function that has a comparator
|
||||||
|
def insert (root, value, key = lambda value: value):
|
||||||
|
if not root:
|
||||||
|
root = TreeNode()
|
||||||
|
root.value = value
|
||||||
|
else:
|
||||||
|
if value == root.value:
|
||||||
|
root.value = value
|
||||||
|
else:
|
||||||
|
if key(value) < key(root.value):
|
||||||
|
root.left = insert(root.left, value, key)
|
||||||
|
else:
|
||||||
|
root.right = insert(root.right, value, key)
|
||||||
|
return root
|
||||||
|
|
||||||
|
# Alternative make tree that allows for a callback
|
||||||
|
# which returns the sorting value.
|
||||||
|
# By default the sort value is the same as the value
|
||||||
|
def make_tree (values, key = lambda word: word):
|
||||||
|
tree = None
|
||||||
|
|
||||||
|
for value in values:
|
||||||
|
tree = insert(tree, value, key)
|
||||||
|
|
||||||
|
return tree
|
||||||
|
|
||||||
|
def clean (word):
|
||||||
|
return word.lower().strip(' .,!?-"()[]‘’“”')
|
||||||
|
|
||||||
|
def not_empty (word):
|
||||||
|
return (word)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
with open(os.path.join(basepath, 'text-to-sort.txt'), 'r') as h:
|
||||||
|
text = h.read()
|
||||||
|
words = text.split(' ')
|
||||||
|
cleaned_words = list(filter(not_empty, map(clean, words)))
|
||||||
|
|
||||||
|
shuffle(cleaned_words)
|
||||||
|
|
||||||
|
visualize(make_tree(cleaned_words), 'text-tree-random')
|
@ -0,0 +1,85 @@
|
|||||||
|
from random import randint
|
||||||
|
|
||||||
|
# Description of a node
|
||||||
|
class TreeNode (object):
|
||||||
|
def __init__ (self, value = None):
|
||||||
|
self.value = value
|
||||||
|
self.left = None
|
||||||
|
self.right = None
|
||||||
|
|
||||||
|
"""
|
||||||
|
This function defines a root for the tree and initiates
|
||||||
|
the process of looping through the values and inserting
|
||||||
|
them into the tree.
|
||||||
|
"""
|
||||||
|
def make_tree (values):
|
||||||
|
tree = None
|
||||||
|
|
||||||
|
for value in values:
|
||||||
|
tree = insert(tree, value)
|
||||||
|
|
||||||
|
return tree
|
||||||
|
|
||||||
|
"""
|
||||||
|
Adds a node to the tree
|
||||||
|
"""
|
||||||
|
def insert (root, value, key = lambda value: value):
|
||||||
|
# Test whether there is a node at the current location
|
||||||
|
if not root:
|
||||||
|
# If not, insert the node here
|
||||||
|
|
||||||
|
root = TreeNode()
|
||||||
|
root.value = value
|
||||||
|
else:
|
||||||
|
# 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)
|
||||||
|
else:
|
||||||
|
root.right = insert(root.right, value, key)
|
||||||
|
return root
|
||||||
|
|
||||||
|
"""
|
||||||
|
Measures the depth of a (sub)tree
|
||||||
|
"""
|
||||||
|
def measure_tree (node):
|
||||||
|
if node:
|
||||||
|
# If there is a node, measure the depth
|
||||||
|
# of the left and right node
|
||||||
|
depth_left = measure_tree(node.left)
|
||||||
|
depth_right = measure_tree(node.right)
|
||||||
|
|
||||||
|
# Return the answer with the highest value
|
||||||
|
# and add one
|
||||||
|
return max(depth_left, depth_right) + 1
|
||||||
|
else:
|
||||||
|
# There isn't a node, answer with the value 0
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def traverse_tree (node):
|
||||||
|
# Test whether there is a node
|
||||||
|
if node:
|
||||||
|
# If there is a node, ask its left node to traverse
|
||||||
|
tree_left = traverse_tree(node.left)
|
||||||
|
# and ask the same for its right node
|
||||||
|
tree_right = traverse_tree(node.right)
|
||||||
|
|
||||||
|
# Then combine into a new list:
|
||||||
|
# the list answered by the left node
|
||||||
|
# with the value of the node itself
|
||||||
|
# And the list answered by the right node
|
||||||
|
#
|
||||||
|
# Give this new list as an answer to the root node
|
||||||
|
return tree_left + [ node.value ] + tree_right
|
||||||
|
else:
|
||||||
|
# There is no node, return an empty list
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
values = [randint(0, 100) for _ in range(15)]
|
||||||
|
tree = make_tree(values)
|
||||||
|
|
||||||
|
print(values, tree.value, tree.left.value if tree.left else None, tree.right.value if tree.right else None, measure_tree(tree), traverse_tree(tree))
|
@ -0,0 +1,38 @@
|
|||||||
|
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')
|
Loading…
Reference in New Issue