Skip to content

Commit ca06b9d

Browse files
committed
update assignment 1 solution
1 parent b6493b7 commit ca06b9d

File tree

1 file changed

+145
-34
lines changed

1 file changed

+145
-34
lines changed

02_activities/assignments/assignment_1.ipynb

Lines changed: 145 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,25 @@
2121
},
2222
{
2323
"cell_type": "code",
24-
"execution_count": null,
24+
"execution_count": 9,
2525
"metadata": {},
26-
"outputs": [],
26+
"outputs": [
27+
{
28+
"name": "stdout",
29+
"output_type": "stream",
30+
"text": [
31+
"2\n"
32+
]
33+
}
34+
],
2735
"source": [
2836
"import hashlib\n",
2937
"\n",
3038
"def hash_to_range(input_string: str) -> int:\n",
3139
" hash_object = hashlib.sha256(input_string.encode())\n",
3240
" hash_int = int(hash_object.hexdigest(), 16)\n",
3341
" return (hash_int % 3) + 1\n",
34-
"input_string = \"your_first_name_here\"\n",
42+
"input_string = \"fan\"\n",
3543
"result = hash_to_range(input_string)\n",
3644
"print(result)\n"
3745
]
@@ -80,9 +88,18 @@
8088
},
8189
{
8290
"cell_type": "code",
83-
"execution_count": null,
91+
"execution_count": 10,
8492
"metadata": {},
85-
"outputs": [],
93+
"outputs": [
94+
{
95+
"ename": "IndentationError",
96+
"evalue": "expected an indented block (2850872121.py, line 8)",
97+
"output_type": "error",
98+
"traceback": [
99+
"\u001b[0;36m Cell \u001b[0;32mIn[10], line 8\u001b[0;36m\u001b[0m\n\u001b[0;31m # TODO\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mIndentationError\u001b[0m\u001b[0;31m:\u001b[0m expected an indented block\n"
100+
]
101+
}
102+
],
86103
"source": [
87104
"# Definition for a binary tree node.\n",
88105
"# class TreeNode(object):\n",
@@ -130,18 +147,31 @@
130147
},
131148
{
132149
"cell_type": "code",
133-
"execution_count": null,
150+
"execution_count": 27,
134151
"metadata": {},
135152
"outputs": [],
136153
"source": [
137154
"# Definition for a binary tree node.\n",
138-
"# class TreeNode(object):\n",
139-
"# def __init__(self, val = 0, left = None, right = None):\n",
140-
"# self.val = val\n",
141-
"# self.left = left\n",
142-
"# self.right = right\n",
155+
"from typing import List\n",
156+
"\n",
157+
"class TreeNode(object):\n",
158+
" def __init__(self, val = 0, left = None, right = None):\n",
159+
" self.val = val\n",
160+
" self.left = left\n",
161+
" self.right = right\n",
143162
"def bt_path(root: TreeNode) -> List[List[int]]:\n",
144-
" # TODO"
163+
" def dfs(node, path):\n",
164+
" if node:\n",
165+
" path.append(node.val)\n",
166+
" if not node.left and not node.right:\n",
167+
" res.append(path[:])\n",
168+
" else:\n",
169+
" dfs(node.left, path)\n",
170+
" dfs(node.right, path)\n",
171+
" path.pop()\n",
172+
" res = []\n",
173+
" dfs(root, [])\n",
174+
" return res"
145175
]
146176
},
147177
{
@@ -201,12 +231,10 @@
201231
]
202232
},
203233
{
204-
"cell_type": "code",
205-
"execution_count": null,
234+
"cell_type": "markdown",
206235
"metadata": {},
207-
"outputs": [],
208236
"source": [
209-
"# Your answer here"
237+
"Problem: Given a binary tree, I need to find all possible paths from the root node to the leaf nodes, each path should be represented as a list of node values, this is a DFS."
210238
]
211239
},
212240
{
@@ -218,11 +246,52 @@
218246
},
219247
{
220248
"cell_type": "code",
221-
"execution_count": null,
249+
"execution_count": 20,
222250
"metadata": {},
223251
"outputs": [],
224252
"source": [
225-
"# Your answer here"
253+
"def test_bt_path(root: TreeNode, expected: List[str]):\n",
254+
" result = bt_path(root)\n",
255+
" if sorted(result) == sorted(expected):\n",
256+
" print(f\"Test passed! Expected {expected}\")\n",
257+
" else:\n",
258+
" print(f\"Test failed! Expected {expected}, but got {result}\")"
259+
]
260+
},
261+
{
262+
"cell_type": "code",
263+
"execution_count": 28,
264+
"metadata": {},
265+
"outputs": [
266+
{
267+
"name": "stdout",
268+
"output_type": "stream",
269+
"text": [
270+
"Test passed! Expected [[5, 4], [5, 6, 7]]\n",
271+
"Test passed! Expected [[10, 5, 3], [10, 5, 8], [10, 15, 12]]\n"
272+
]
273+
}
274+
],
275+
"source": [
276+
"root_example_3 = TreeNode(5)\n",
277+
"root_example_3.left = TreeNode(4)\n",
278+
"root_example_3.right = TreeNode(6)\n",
279+
"root_example_3.right.left = TreeNode(7)\n",
280+
" \n",
281+
"expected_example_3 = [[5,4], [5,6,7]]\n",
282+
"test_bt_path(root_example_3, expected_example_3)\n",
283+
" \n",
284+
"\n",
285+
"root_example_4 = TreeNode(10)\n",
286+
"root_example_4.left = TreeNode(5)\n",
287+
"root_example_4.right = TreeNode(15)\n",
288+
"root_example_4.left.left = TreeNode(3)\n",
289+
"root_example_4.left.right = TreeNode(8)\n",
290+
"root_example_4.right.left = TreeNode(12)\n",
291+
" \n",
292+
"expected_example_4 = [[10, 5, 3], [10, 5, 8], [10, 15, 12]]\n",
293+
" \n",
294+
"test_bt_path(root_example_4, expected_example_4)"
226295
]
227296
},
228297
{
@@ -239,7 +308,19 @@
239308
"metadata": {},
240309
"outputs": [],
241310
"source": [
242-
"# Your answer here"
311+
"def bt_path(root: TreeNode) -> List[List[int]]:\n",
312+
" def dfs(node, path):\n",
313+
" if node:\n",
314+
" path.append(node.val)\n",
315+
" if not node.left and not node.right:\n",
316+
" res.append(path[:])\n",
317+
" else:\n",
318+
" dfs(node.left, path)\n",
319+
" dfs(node.right, path)\n",
320+
" path.pop()\n",
321+
" res = []\n",
322+
" dfs(root, [])\n",
323+
" return res"
243324
]
244325
},
245326
{
@@ -251,12 +332,21 @@
251332
]
252333
},
253334
{
254-
"cell_type": "code",
255-
"execution_count": null,
335+
"cell_type": "markdown",
256336
"metadata": {},
257-
"outputs": [],
258337
"source": [
259-
"# Your answer here"
338+
"-----------\n",
339+
"The solution uses a recursive DFS to explore all paths from the root to the leaves. Here’s how it works:\n",
340+
"- **DFS Helper Function**: dfs(node, path, res) takes:\n",
341+
" - node: Current node being processed.\n",
342+
" - path: A list storing the current path’s node values as strings.\n",
343+
" - res: The result list collecting all complete paths.\n",
344+
"- **Base Case**: If node is None (empty subtree), return without doing anything.\n",
345+
"- **Path Building**: Add the current node’s value to path. This tracks the sequence from root to the current node.\n",
346+
"- **Leaf Check**: If the node has no left or right child (not node.left and not node.right), it’s a leaf. push to res []\n",
347+
"- **Recursion**: Recursively call dfs on the left and right children to explore all possible paths.\n",
348+
"- **Backtrack**: path.pop() backtracks by removing the last element from the path list, allowing the algorithm to go back to previous state and explore other paths.\n",
349+
"-----------"
260350
]
261351
},
262352
{
@@ -268,29 +358,50 @@
268358
]
269359
},
270360
{
271-
"cell_type": "code",
272-
"execution_count": null,
361+
"cell_type": "markdown",
273362
"metadata": {},
274-
"outputs": [],
275363
"source": [
276-
"# Your answer here"
364+
"-----------\n",
365+
"Time Complexity: O(N*logN). \n",
366+
"- DFS Traversal: This solution visits each node exactly once, so the time complexity is (O(N)), where (N) is the number of nodes in the binary tree. \n",
367+
"- Path Construct: For each path, we append and pop values from the path list, where LogN is the length of the path, and height of the tree (H).\n",
368+
"- Overall time complexity remains (O(N * H))\n",
369+
"\n",
370+
"Space Complexity: O(N + H). \n",
371+
"- Recursive Stack: The space complexity due to the recursive stack is (O(H)) where (H) is the height of the tree.\n",
372+
"- Path List: We use a list path to store the current path, which can take up to the length of the longest path, i.e., (O(H)).\n",
373+
"- Result List: The result list res stores all paths, and in the worst case, it could contain all possible paths from root to leaf nodes, which is (O(N)).\n",
374+
"- Overall space complexity is (O(N + H)).\n",
375+
" \n",
376+
"-----------"
277377
]
278378
},
279379
{
280380
"cell_type": "markdown",
281381
"metadata": {},
282382
"source": [
283383
"\n",
284-
"- Explain the thinking to an alternative solution (no coding required, but a classmate reading this should be able to code it up based off your text)\n"
384+
" - Explain the thinking to an alternative solution (no coding required, but a classmate reading this should be able to code it up based off your text)\n"
285385
]
286386
},
287387
{
288-
"cell_type": "code",
289-
"execution_count": null,
388+
"cell_type": "markdown",
290389
"metadata": {},
291-
"outputs": [],
292390
"source": [
293-
"# Your answer here"
391+
"-----------\n",
392+
"To solve this problem iteratively using a stack:\n",
393+
"\n",
394+
"1. Initialize Stack: Start with the root node, an empty path list.\n",
395+
"2. Iterate While Stack is Not Empty:\n",
396+
" 1. Pop the current node, its path, and whether it's a leaf from the stack.\n",
397+
" 2. If the current node is a leaf, add the constructed path to the result list.\n",
398+
" 3. If the current node has a right child, push it onto the stack with the updated path and mark it as not a leaf.\n",
399+
" 4. If the current node has a left child, push it onto the stack with the updated path and mark it as not a leaf.\n",
400+
"3. Return Result List: After processing all nodes, return the result list containing all paths.\n",
401+
"\n",
402+
"This iterative approach ensures that each node is visited exactly once and constructs the paths efficiently using a stack.\n",
403+
"\n",
404+
"-----------"
294405
]
295406
},
296407
{
@@ -338,7 +449,7 @@
338449
],
339450
"metadata": {
340451
"kernelspec": {
341-
"display_name": "Python 3",
452+
"display_name": "dsi_participant",
342453
"language": "python",
343454
"name": "python3"
344455
},
@@ -352,7 +463,7 @@
352463
"name": "python",
353464
"nbconvert_exporter": "python",
354465
"pygments_lexer": "ipython3",
355-
"version": "3.11.7"
466+
"version": "3.9.7"
356467
}
357468
},
358469
"nbformat": 4,

0 commit comments

Comments
 (0)