Skip to content
Snippets Groups Projects
Commit cf73b278 authored by Romain Vuillemot's avatar Romain Vuillemot
Browse files

Update 08-binary-trees-traversal-exercises.ipynb

parent 8e463a23
Branches
No related tags found
No related merge requests found
%% Cell type:markdown id:e14a9e35-e497-4765-8089-95d0db59f82d tags:
# UE5 Fundamentals of Algorithms
# Lab 8: Binary trees traversals
%% Cell type:markdown id:da448b8a-05e3-4ee6-b3b8-6fa4c41f55b6 tags:
---
%% Cell type:markdown id:d3c4bf88-8101-42bb-a14d-9e5607b55d57 tags:
We will use the following binary tree data structure (unless specified otherwise):
%% Cell type:code id:2b179632-5d9e-4abd-95c1-abfea9d455df tags:
``` python
class Node:
def __init__(self, value):
self.value = value
self.left = None
self.right = None
```
%% Cell type:markdown id:1d18a7cb-9003-4d84-a68d-dfbd8202713b tags:
## Exercise 1: Check if a binary tree is balanced
%% Cell type:markdown id:ac3a3e11-4a8d-416c-8b55-3c6a4137a457 tags:
Write an **iterative** function that checks if a binary tree is balanced, i.e. the difference between the heights of the left and right subtrees is at most 1.
Tips:
- Use af dfs traversal approach (using a stack) using a post-order approach (left, right, and root node)
- For each node, calculate and memorize the current height in a dictionnary
- Check if the current node is balanced, if not return `False`
- Mark the node as visited and store its height
- Return `True` if all the nodes are visited (without an un-balanced situation)
%% Cell type:code id:51a49306-6cb7-4e84-9257-081793657848 tags:
``` python
def is_balanced_ite(root):
### BEGIN SOLUTION
if root is None:
return True
# stack for post-order traversal
stack = []
# heights dictionnary
node_heights = {}
current = root
last_visited = None
while stack or current:
if current:
stack.append(current)
current = current.left
else:
peek_node = stack[-1]
if peek_node.right and last_visited != peek_node.right:
current = peek_node.right
else:
# heights of left and right children
left_height = node_heights.get(peek_node.left, -1)
right_height = node_heights.get(peek_node.right, -1)
# balance for current node
if abs(left_height - right_height) > 1:
return False
# height of the current node
node_heights[peek_node] = max(left_height, right_height) + 1
# mark the node as visited
last_visited = stack.pop()
return True
### END SOLUTION
```
%% Cell type:markdown id:556f6692-1e4f-4f55-adbc-cf373402673d tags:
Compare to the following recursive version:
%% Cell type:code id:b7fb2e89-b045-4a46-851e-153c64e0de60 tags:
``` python
def get_height(node):
if node is None:
return -1
left_height = get_height(node.left)
right_height = get_height(node.right)
return max(left_height, right_height) + 1
def is_balanced(root):
# un abre vide (None) est équilibré
if root is None:
return True
return is_balanced(root.right) and is_balanced(root.left) and abs(get_height(root.left) - get_height(root.right)) <= 1
#get_height(racine)
#is_balanced(racine)
```
%% Cell type:markdown id:00e2ddec-e259-4f8b-a414-a11051672173 tags:
Write tests with both balanced and unbalanced trees.
%% Cell type:code id:f90da531-0173-429a-a415-923e7b2bf275 tags:
``` python
### BEGIN SOLUTION
balanced = Node(1)
balanced.left = Node(2)
balanced.right = Node(3)
balanced.left.left = Node(4)
balanced.left.right = Node(5)
assert is_balanced(balanced)
unbalanced = Node(1)
unbalanced.left = Node(2)
unbalanced.right = Node(3)
unbalanced.left.left = Node(4)
unbalanced.left.right = Node(5)
unbalanced.right.left = Node(6)
unbalanced.right.left.left = Node(7)
assert not is_balanced(unbalanced)
### END SOLUTION
```
%% Cell type:markdown id:09013922-8606-4298-91e5-02da49a8ced2 tags:
## Exercise 2: DFS traversal orders
Given the following tree, which type of dfs traversal order you may use to return the number in ascending order?
```
1
/ \
2 5
/ \
3 4
```
%% Cell type:code id:afa39767-9b92-4eb7-8a2c-7b59715153da tags:
``` python
def inorder_traversal(root):
if root is not None:
inorder_traversal(root.left)
print(root.value, end=' ')
inorder_traversal(root.right)
```
%% Cell type:code id:e88833c1-3cf0-4751-bf79-d7ba410a3dd2 tags:
``` python
def preorder_traversal(root):
if root is not None:
print(root.value, end=' ')
preorder_traversal(root.left)
preorder_traversal(root.right)
```
%% Cell type:code id:fdff0e4c-e6f1-4185-b7dc-92aa3f8f7ccb tags:
``` python
def postorder_traversal(root):
if root is not None:
postorder_traversal(root.left)
postorder_traversal(root.right)
print(root.value, end=' ')
```
%% Cell type:code id:78fbbaa9-a66f-4edc-ad6d-35364efcf83a tags:
``` python
### BEGIN SOLUTION
tree = Node(1)
tree.left = Node(2)
tree.right = Node(5)
tree.left.left = Node(3)
tree.left.right = Node(4)
preorder_traversal(tree)
order = "Pre-order traversal: 1, 2, 4, 5, 3"
### END SOLUTION
```
%% Output
1 2 3 4 5
%% Cell type:markdown id:0ab0e8cb-0f07-4df4-a1e1-8934b9fa9de2 tags:
Now write the corresponding tree for the 2 other traversal functions to return values in ascending order (i.e. provide a new tree but do not change the functions)
%% Cell type:code id:498cd602-5e9e-456b-b66a-4b289442170f tags:
``` python
### BEGIN SOLUTION
tree = Node(3)
tree.left = Node(2)
tree.right = Node(4)
tree.left.left = Node(1)
tree.right.right = Node(5)
inorder_traversal(tree)
### END SOLUTION
```
%% Output
1 2 3 4 5
%% Cell type:code id:13dd6b6f-e843-4a92-af0e-2c860d95cf40 tags:
``` python
### BEGIN SOLUTION
tree = Node(5)
tree.right = Node(4)
tree.left = Node(3)
tree.left.right = Node(2)
tree.left.left = Node(1)
postorder_traversal(tree)
### END SOLUTION
```
%% Output
1 2 3 4 5
%% Cell type:markdown id:098f76aa-1597-4394-a7d7-564e5e1949cf tags:
## Exercise 3: Check if two binary trees are identical
```
1 1
/ \ / \
2 3 2 3
```
For the example above, it may return `True`
%% Cell type:code id:49aa6fce-c267-4ac1-af06-085ab33cfb4f tags:
``` python
def check_equal(p, q):
### BEGIN SOLUTION
if p == None and q == None:
return True
elif p == None or q == None:
return False
elif p.value == q.value:
return check_equal(p.left, q.left) and check_equal(p.right, q.right)
else:
return False
### END SOLUTION
```
%% Cell type:code id:d34397b0-e505-410f-9edb-75d6f9f35b32 tags:
``` python
p = Node(1)
q = Node(1)
assert check_equal(p, q)
```
%% Cell type:markdown id:8d65c14b tags:
## Exercise 4: Check if a binary tree has a cycle
Write a function to determine if a binary tree contains a cycle (i.e. when a node is revisited during traversal). Write a recursive version first.
%% Cell type:code id:3e5a8a2d tags:
``` python
def has_cycle(node, visited=None, parent=None):
### BEGIN SOLUTION
if visited is None:
visited = set()
if node is None:
return False
if node in visited:
return True
visited.add(node)
if node.left is not parent:
if has_cycle(node.left, visited, node):
return True
if node.right is not parent:
if has_cycle(node.right, visited, node):
return True
return False
### END SOLUTION
```
%% Cell type:markdown id:3e5b5304-976b-429d-9d94-469195224234 tags:
Write an iterative version now.
%% Cell type:code id:bcab37fd-214b-4bdb-ae63-1e4b93411f3d tags:
``` python
def has_cycle_iterative(node):
### BEGIN SOLUTION
if not node:
return False
visited = set()
stack = [node]
while stack:
current = stack.pop()
if current in visited:
return True
visited.add(current)
# Add left and right children to the stack if not visited
if current.left and current.left not in visited:
stack.append(current.left)
if current.right and current.right not in visited:
stack.append(current.right)
return False
### END SOLUTION
```
%% Cell type:code id:83f55a68 tags:
``` python
root = Node(1)
root.left = Node(2)
root.right = Node(3)
root.left.left = Node(4)
root.left.right = Node(5)
assert not has_cycle(root)
# add a cycle
root.left.left.right = root
assert has_cycle(root)
```
%% Cell type:markdown id:1aa5b552-aaa6-4f97-921e-fcc0135ad485 tags:
## Exercise 5: Check if a binary tree is connected
Write a function to determine if a binary is connected to all the nodes of the tree. Warning: will now use a dictionnary data structure to store the tree.
%% Cell type:code id:8ba8a250-70f7-4ec8-9deb-eea0b3f34ca3 tags:
``` python
T_dict = {
'0': ['1', '2'],
'1': ['4', '5'],
'2': ['3'],
'4': [],
'5': [],
'3': []
}
```
%% Cell type:code id:0460d5e5-d9b1-4418-a58b-0e00a481ed67 tags:
``` python
def is_connected(T_dict):
### BEGIN SOLUTION
all_nodes = set(T_dict.keys())
if not all_nodes:
return True
root = '0'
visited = set()
stack = [root]
while stack:
node = stack.pop()
if node not in visited:
visited.add(node)
stack.extend(T_dict.get(node, []))
return visited == all_nodes
### END SOLUTION
```
%% Cell type:markdown id:d8298892-488a-482c-98bd-2a16d02f29a6 tags:
Write tests for bot a connected and a non-connected tree.
%% Cell type:code id:55ab4951-be83-48ee-a6c5-85d6e73bdf05 tags:
``` python
### BEGIN SOLUTION
T_dict = {
'0': ['1', '2'],
'1': ['4', '5'],
'2': ['3'],
'4': [],
'5': [],
'3': []
}
assert is_connected(T_dict)
T_dict['2'] = []
assert not is_connected(T_dict)
### END SOLUTION
```
%% Cell type:markdown id:c06c33fb-661b-4a73-84f7-c19c5c157fae tags:
## Exercise 6: Calculate the diameter of a binary tree
To find the diameter of a binary tree, you need to compute the longest path between any two nodes in the tree. This path may or may not pass through the root. Here is a way to calculate the diameter:
1. Calculate the height of the left and right subtrees for each node
2. Calculate the diameter at each node as the height of left subtree + right subtree
3. Traverse the tree and keep track of the max diameter encountered
Note: we will used the `Node` data structure.
%% Cell type:code id:c2595aa9-d477-48c8-9aaa-9bc331930877 tags:
``` python
def height(root):
if root is None:
return 0
return 1 + max(height(root.left), height(root.right))
def diameter(root):
### BEING SOLUTION
if root is None:
return 0
lheight = height(root.left)
rheight = height(root.right)
ldiameter = diameter(root.left)
rdiameter = diameter(root.right)
return max(lheight + rheight, ldiameter, rdiameter)
### END SOLUTION
```
%% Cell type:code id:e21f200a-ff9b-46af-b504-876c47b80f48 tags:
``` python
root = Node(1)
root.left = Node(2)
root.right = Node(3)
root.left.left = Node(4)
root.left.right = Node(5)
assert diameter(root) == 3 # 3
```
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment