14.3. Trees & Graphs#
Trees and graphs organize relationships. Trees model hierarchy; graphs model general connections.
Outline
Trees
o Root, children, leaves
o Recursive traversal
Graphs
o Nodes and edges
o Adjacency lists
Graph Traversal
o Breadth-first search
o Depth-first search
Summary
14.3.1. Trees#
A tree is a linked structure with a root and branches. Each node can have children. A node with no children is called a leaf.
Trees are useful for hierarchical data such as folders, organization charts, HTML documents, decision trees, and expression trees.
class TreeNode:
def __init__(self, value):
self.value = value
self.children = []
def add_child(self, child):
self.children.append(child)
root = TreeNode("company")
analytics = TreeNode("analytics")
operations = TreeNode("operations")
root.add_child(analytics)
root.add_child(operations)
analytics.add_child(TreeNode("alice"))
analytics.add_child(TreeNode("bob"))
operations.add_child(TreeNode("charlie"))
def print_tree(node, level=0):
indent = " " * level
print(indent + str(node.value))
for child in node.children:
print_tree(child, level + 1)
print_tree(root)
company
analytics
alice
bob
operations
charlie
### Exercise: Add a Tree Node
# Add a new employee named dana under operations.
# Then print the tree again.
### Your code starts here.
### Your code ends here.
company
analytics
alice
bob
operations
charlie
dana
14.3.2. Graphs#
A graph stores relationships between items. The items are nodes. The relationships are edges.
A common Python representation is an adjacency list: a dictionary where each key maps to a list of neighbors.
graph = {
"alice": ["bob", "charlie"],
"bob": ["alice", "dana"],
"charlie": ["alice", "dana"],
"dana": ["bob", "charlie"],
}
print(graph["alice"])
['bob', 'charlie']
### Exercise: Add a Graph Edge
# Add a new person named erin who is connected to dana.
# Update both adjacency lists so the relationship works both ways.
# Then print graph["dana"].
### Your code starts here.
### Your code ends here.
['bob', 'charlie', 'erin']
14.3.3. Graph Traversal#
A traversal visits nodes systematically.
Breadth-first search uses a queue and explores nearby nodes first.
Depth-first search uses a stack or recursion and follows one path before backing up.
from collections import deque
def breadth_first_search(graph, start):
visited = set()
order = []
queue = deque([start])
while queue:
node = queue.popleft()
if node in visited:
continue
visited.add(node)
order.append(node)
for neighbor in graph.get(node, []):
if neighbor not in visited:
queue.append(neighbor)
return order
print(breadth_first_search(graph, "alice"))
['alice', 'bob', 'charlie', 'dana', 'erin']
### Exercise: Stop at a Target
# Modify breadth_first_search so it accepts a target node.
# The function should return the visited order once the target is found.
### Your code starts here.
### Your code ends here.
['alice', 'bob', 'charlie', 'dana', 'erin']
14.3.4. Summary#
Trees model hierarchy. Graphs model general relationships. Traversal algorithms make these structures useful because they provide repeatable ways to visit connected data.