"**IMPORTANT:** make sure the graphviz library (to visualize graphs) runs in your notebooks, this can be achieved by running the following cells (if graphviz is not install run the `!pip install graphviz` command in a cell). If graphviz ultimatelly does not, then you may skip the cell with the `visualize_oop` functions."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1f8b2afd-8315-41fa-b5d7-7f9566332151",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"from graphviz import Digraph\n",
"from IPython.display import display\n",
"\n",
"class Node:\n",
" def __init__(self, value, l = None, r = None):\n",
"## Exercise 1: Manipulate binary trees data structure\n",
"\n",
"Here is an example of tree (using the graphviz library as data structure). Look at the result."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "83b91119-09f7-4cba-a87b-19fc4a793e91",
"metadata": {},
"outputs": [],
"source": [
"from graphviz import Digraph\n",
"\n",
"dot = Digraph()\n",
"\n",
"dot.node_attr['shape'] = 'circle'\n",
"\n",
"dot.node('0', label='0') # Root\n",
"dot.node('1')\n",
"dot.node('2')\n",
"dot.node('3')\n",
"dot.node('4')\n",
"dot.node('5')\n",
"\n",
"dot.edge('0', '1')\n",
"dot.edge('1', '4')\n",
"dot.edge('1', '5')\n",
"\n",
"dot.edge('0', '2', color='red')\n",
"dot.edge('2', '3', color='red')\n",
"\n",
"\n",
"dot # Render the graph"
]
},
{
"cell_type": "markdown",
"id": "1fca57f5-4b3c-4ad8-a4c0-9d1de647abf9",
"metadata": {
"tags": []
},
"source": [
"Use a `Dict` data structure to store the nodes and edges of the above tree."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fa1bce0f-7b06-4d20-8a1b-990bcd03bda9",
"metadata": {
"deletable": false,
"nbgrader": {
"cell_type": "code",
"checksum": "e4c7bf49dab0dfd7b602a4c0c683b731",
"grade": false,
"grade_id": "cell-0f290aab7c180fb7",
"locked": false,
"schema_version": 3,
"solution": true,
"task": false
},
"tags": []
},
"outputs": [],
"source": [
"# YOUR CODE HERE\n",
"raise NotImplementedError()"
]
},
{
"cell_type": "markdown",
"id": "cb16382b-e00d-498a-bab2-0b251f93d147",
"metadata": {},
"source": [
"Use a `Tuple` data structure to store the nodes and edges of the above tree."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a4cbc1d4-5adc-4cda-9c2a-24101bd1bed9",
"metadata": {
"deletable": false,
"nbgrader": {
"cell_type": "code",
"checksum": "7738eb399dd344478be29fefebaf1ec2",
"grade": false,
"grade_id": "cell-ce11e1828bd74e71",
"locked": false,
"schema_version": 3,
"solution": true,
"task": false
},
"tags": []
},
"outputs": [],
"source": [
"# YOUR CODE HERE\n",
"raise NotImplementedError()"
]
},
{
"cell_type": "markdown",
"id": "483af12c-f194-4377-a0f4-56f860b6d865",
"metadata": {},
"source": [
"Use a `List` data structure to store the nodes and edges of the above tree."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "54f96c40-40f1-4838-9861-3f08bd828503",
"metadata": {
"deletable": false,
"nbgrader": {
"cell_type": "code",
"checksum": "62d5d16b98feb5512897ee2987d8dd67",
"grade": false,
"grade_id": "cell-2ee2e0e27be77e41",
"locked": false,
"schema_version": 3,
"solution": true,
"task": false
},
"tags": []
},
"outputs": [],
"source": [
"# YOUR CODE HERE\n",
"raise NotImplementedError()"
]
},
{
"cell_type": "markdown",
"id": "5554f67b-ba9d-4143-8082-382fd43caf0b",
"metadata": {},
"source": [
"Write code to compare the various data structure and show they store the same information."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b2144bf6-00d9-40c6-973e-734fad8aa949",
"metadata": {
"deletable": false,
"nbgrader": {
"cell_type": "code",
"checksum": "dd88ac1d56cec16853249057666f0761",
"grade": false,
"grade_id": "cell-1fd6fcebcffa3cbe",
"locked": false,
"schema_version": 3,
"solution": true,
"task": false
},
"tags": []
},
"outputs": [],
"source": [
"# YOUR CODE HERE\n",
"raise NotImplementedError()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e6f07240-86c1-46f7-97f1-4764328b2221",
"metadata": {},
"outputs": [],
"source": [
"Write a function that converts a binary tree stored as a `Dict` in to a `List`."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d7976e73-44e0-4c9c-8383-3acb6f4ed284",
"metadata": {
"deletable": false,
"nbgrader": {
"cell_type": "code",
"checksum": "ad9718713f21cfe278c4e66a062bd4dc",
"grade": false,
"grade_id": "cell-3ce83537aa56f88e",
"locked": false,
"schema_version": 3,
"solution": true,
"task": false
},
"tags": []
},
"outputs": [],
"source": [
"# YOUR CODE HERE\n",
"raise NotImplementedError()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cdec902d-c43c-4be2-bafe-78baac70c7ee",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"dict_to_list(T_dict)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "133cca89-39f7-43c5-8d9f-c0e7c9a89427",
"metadata": {
"deletable": false,
"nbgrader": {
"cell_type": "code",
"checksum": "13b8913979ae8b841e5191d9a5307cf8",
"grade": false,
"grade_id": "cell-8c05491d13ceb894",
"locked": false,
"schema_version": 3,
"solution": true,
"task": false
},
"tags": []
},
"outputs": [],
"source": [
"# YOUR CODE HERE\n",
"raise NotImplementedError()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a06bc87a-dd09-4513-8d73-3f949374f158",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"list_to_dict(dict_to_list_of_lists(T_dict))"
]
},
{
"cell_type": "markdown",
"id": "57c4ed49-f64b-4dc9-80e6-2c80269ebe90",
"metadata": {},
"source": [
"Provide examples of conversions showing they do not lose any information."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "16d722bc-625e-4750-96a4-f58aaab467b1",
"metadata": {
"deletable": false,
"nbgrader": {
"cell_type": "code",
"checksum": "c1b16204d6eec420f608da30da30ff3f",
"grade": false,
"grade_id": "cell-f4064d762be03ca3",
"locked": false,
"schema_version": 3,
"solution": true,
"task": false
},
"tags": []
},
"outputs": [],
"source": [
"# YOUR CODE HERE\n",
"raise NotImplementedError()"
]
},
{
"cell_type": "markdown",
"id": "fb0e9336-603c-49c4-9617-9ccecdbf7156",
"metadata": {},
"source": [
"## Exercise 2: Binary search tree (BST)\n",
"\n",
"We assume we have a binary search tree (BST). Its main property is that for every node, the value of the left children is less than the value of the right children."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "02513514-0e1b-4c0d-a5fd-ac6b571231b7",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"class Node:\n",
" def __init__(self, value):\n",
" self.value = value\n",
" self.left = None\n",
" self.right = None\n",
" def __str__(self):\n",
" return str(self.value)"
]
},
{
"cell_type": "markdown",
"id": "055d2cea-4e45-402d-baf8-e50939c94132",
"metadata": {},
"source": [
"Write an `insert` function inserts a value in a tree so it preserves the BST property (in an iterative way)."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6492983d-054c-4488-b8ff-57b3878f5b7e",
"metadata": {
"deletable": false,
"nbgrader": {
"cell_type": "code",
"checksum": "6033bc2e64682eca39dd7a2dc0739762",
"grade": false,
"grade_id": "cell-b636f3646835e810",
"locked": false,
"schema_version": 3,
"solution": true,
"task": false
},
"tags": []
},
"outputs": [],
"source": [
"def insert_ite(root, value):\n",
" # YOUR CODE HERE\n",
" raise NotImplementedError()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7c2a2f74-a8b8-4cd6-a881-9181efdb7a64",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"a = Node(2)\n",
"insert_ite(a, 3)\n",
"insert_ite(a, 4)\n",
"insert_ite(a, 1)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "10b6d8de-fb38-41e3-92b9-957f04a6f1e4",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"visualize_oop(a)"
]
},
{
"cell_type": "markdown",
"id": "0cb5f4e8-348d-4ab0-bdd3-1fe61a6ca687",
"metadata": {},
"source": [
"Now in a recursive way."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3826d528-370f-4e29-8217-20a71947b39a",
"metadata": {
"deletable": false,
"nbgrader": {
"cell_type": "code",
"checksum": "c3f83333288bee744a4cbbe8d447f4cd",
"grade": false,
"grade_id": "cell-447db421e8ed5090",
"locked": false,
"schema_version": 3,
"solution": true,
"task": false
},
"tags": []
},
"outputs": [],
"source": [
"def insert_rec(root, value):\n",
" # YOUR CODE HERE\n",
" raise NotImplementedError()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "dba5b7d0-1bb6-4b46-b123-5be447b8e10c",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"a = Node(2)\n",
"insert(a, 3)\n",
"insert(a, 4)\n",
"insert(a, 1)"
]
},
{
"cell_type": "markdown",
"id": "d40d2486-c901-428a-b2e3-0004ae707376",
"metadata": {},
"source": [
"Compare the results."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "069fb592-4178-405b-8d8b-546bc908dc1e",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"visualize_oop(a)"
]
},
{
"cell_type": "markdown",
"id": "1608511b-df49-43fd-a946-606cf6bb57a4",
"metadata": {},
"source": [
"Now write a function that search for a given `value` is in the BST. "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "bb343233-16f8-4441-8e18-9407611d8d10",
"metadata": {
"deletable": false,
"nbgrader": {
"cell_type": "code",
"checksum": "8d579646d49a3d3b503e0008925fed08",
"grade": false,
"grade_id": "cell-4569f50d2fd946d3",
"locked": false,
"schema_version": 3,
"solution": true,
"task": false
},
"tags": []
},
"outputs": [],
"source": [
"def search(root, value):\n",
" # YOUR CODE HERE\n",
" raise NotImplementedError()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "05aaa6a3-a57c-47b3-849f-722ee0546ab0",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"assert search(a, 3)\n",
"assert not search(a, 5)"
]
},
{
"cell_type": "markdown",
"id": "07ca3602-96fd-43d7-b7ac-54f92124a11d",
"metadata": {
"tags": []
},
"source": [
"## Exercise 3: Calculate properties of binary trees"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2e65ec3d-8e90-4e25-befb-d779713fa3f2",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"class Node:\n",
" def __init__(self, value, l = None, r = None):\n",
" self.value = value\n",
" self.left = l\n",
" self.right = r"
]
},
{
"cell_type": "markdown",
"id": "3bd05d7e-7e0c-4042-9aac-616960849de9",
"metadata": {},
"source": [
"Calculate the height of a binary tree in a recursive way:"
"A binary tree is considered balanced if the height of the left and right subtrees of any node differ by no more than 1. You may use the previously created `height` function."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d15492f9-0f52-4f1c-b782-fced561764d0",
"metadata": {
"deletable": false,
"nbgrader": {
"cell_type": "code",
"checksum": "1ff5b565b8ab1602760d14d61d1dbbc4",
"grade": false,
"grade_id": "cell-89fcd7b808be54c9",
"locked": false,
"schema_version": 3,
"solution": true,
"task": false
},
"tags": []
},
"outputs": [],
"source": [
"def is_balanced_rec(root):\n",
" # YOUR CODE HERE\n",
" raise NotImplementedError()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0d7542de-86a9-4e38-ba51-87f55d6f7909",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"assert is_balanced_rec(Node(2))\n",
"assert not is_balanced_rec(Node(2, Node(3, Node(4))))"
]
},
{
"cell_type": "markdown",
"id": "6c613015-7ab9-48a9-9c58-a9ee0fcb8c7c",
"metadata": {},
"source": [
"## Exercise 4: Implement a (complete) binary tree using a list"
]
},
{
"cell_type": "markdown",
"id": "0c98803c-4d6d-4c8a-b496-bcc49fa48232",
"metadata": {
"tags": []
},
"source": [
"We will now implement a **complete binary tree**. This binary tree will be implemented using an array (since it is a **complete** tree where all levels are filled, except possibly the last). The binary tree has nodes with an index \\(i\\), with a left child and a right child. The array and the tree are connected as follows:\n",
"\n",
"- The root is at position $i = 0$ (this value will be returned by the function `get_root`).\n",
"- The parent is at position $\\lfloor (i - 1)/ 2 \\rfloor$ (function `get_parent`).\n",
"- The left child is at position $2 \\times i + 1$ (function `get_left_child`).\n",
"- The right child is at position $2 \\times i + 2$ (function `get_right_child`).\n",
**IMPORTANT:** make sure the graphviz library (to visualize graphs) runs in your notebooks, this can be achieved by running the following cells (if graphviz is not install run the `!pip install graphviz` command in a cell). If graphviz ultimatelly does not, then you may skip the cell with the `visualize_oop` functions.
We assume we have a binary search tree (BST). Its main property is that for every node, the value of the left children is less than the value of the right children.
A binary tree is considered balanced if the height of the left and right subtrees of any node differ by no more than 1. You may use the previously created `height` function.
We will now implement a **complete binary tree**. This binary tree will be implemented using an array (since it is a **complete** tree where all levels are filled, except possibly the last). The binary tree has nodes with an index \(i\), with a left child and a right child. The array and the tree are connected as follows:
- The root is at position $i = 0$ (this value will be returned by the function `get_root`).
- The parent is at position $\lfloor (i - 1)/ 2 \rfloor$ (function `get_parent`).
- The left child is at position $2 \times i + 1$ (function `get_left_child`).
- The right child is at position $2 \times i + 2$ (function `get_right_child`).