From 70b1786d89af2deb1c68f9a5a7a05e37d3475528 Mon Sep 17 00:00:00 2001 From: Lisa Feeney Date: Fri, 15 Nov 2024 17:37:03 -0500 Subject: [PATCH] Assignment_1 --- 02_activities/assignments/assignment_1.ipynb | 150 +++++++++++++++++-- 1 file changed, 139 insertions(+), 11 deletions(-) diff --git a/02_activities/assignments/assignment_1.ipynb b/02_activities/assignments/assignment_1.ipynb index fc53cf8..fb07e52 100644 --- a/02_activities/assignments/assignment_1.ipynb +++ b/02_activities/assignments/assignment_1.ipynb @@ -82,8 +82,8 @@ "# self.val = val\n", "# self.left = left\n", "# self.right = right\n", - "def is_duplicate(root: TreeNode) -> int:\n", - " # TODO" + "# def is_duplicate(root: TreeNode) -> int:\n", + " # TODO\n" ] }, { @@ -198,7 +198,23 @@ "metadata": {}, "outputs": [], "source": [ - "# Your answer here" + "# We are looking to solve the problem of identifying the first duplicate value in a binary tree \n", + "# using breadth-first traversal (BFT) \n", + "\n", + "# BFT:\n", + "# Step 1: If the tree is empty, it immediately returns -1 (no duplicates exist)\n", + "# Step 2: The function then goes through each node in the tree level by level.\n", + "# For each node, it checks to see if the node’s value has been seen before:\n", + "# If the value exists in the seen set, it returns that value immediately, as the first duplicate.\n", + "# If the value hasn't been seen, it adds it to the seen set and continues.\n", + "# Step 3: It then enqueues the node’s left and right children, if they exist, for further traversal. \n", + "# Step 4: If no duplicates are found by traversal completion, it returns -1 \n", + "\n", + "# Even simplier explanation:\n", + "# We are given the root of a binary tree, and asked to check whether it is contains a duplicate value. \n", + "# If a duplicate exists, it returns the duplicate value. \n", + "# If there are multiple duplicates, it returns the one with the closest distance to the root. \n", + "# If no duplicate exists, it returns -1.\n" ] }, { @@ -212,9 +228,60 @@ "cell_type": "code", "execution_count": null, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "9\n" + ] + } + ], "source": [ - "# Your answer here" + "# Input: root = [1, 2, 3, 4, 5, 9, 9] \n", + "# Output: 9\n", + "\n", + "# Create the tree nodes\n", + "root = TreeNode(1)\n", + "root.left = TreeNode(2)\n", + "root.right = TreeNode(3)\n", + "root.left.left = TreeNode(4)\n", + "root.left.right = TreeNode(5)\n", + "root.right.left = TreeNode(9)\n", + "root.right.right = TreeNode(9)\n", + "\n", + "# Run the function\n", + "print(is_duplicate(root))" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "9\n" + ] + } + ], + "source": [ + "# Input: root = [1, 9, 9, 4, 5, 7, 8] \n", + "# Output: 9\n", + "\n", + "# Create the tree nodes\n", + "root = TreeNode(1)\n", + "root.left = TreeNode(9)\n", + "root.right = TreeNode(9)\n", + "root.left.left = TreeNode(4)\n", + "root.left.right = TreeNode(5)\n", + "root.right.left = TreeNode(7)\n", + "root.right.right = TreeNode(8)\n", + "\n", + "# Run the function\n", + "print(is_duplicate(root))" ] }, { @@ -231,7 +298,38 @@ "metadata": {}, "outputs": [], "source": [ - "# Your answer here" + "from collections import deque\n", + "\n", + "# Definition for a binary tree node.\n", + "class TreeNode:\n", + " def __init__(self, val=0, left=None, right=None):\n", + " self.val = val\n", + " self.left = left\n", + " self.right = right\n", + "\n", + "def is_duplicate(root: TreeNode) -> int:\n", + " if not root:\n", + " return -1\n", + "\n", + " seen = set() # To store seen values\n", + " queue = deque([root]) # Queue for level-order traversal\n", + "\n", + " while queue:\n", + " node = queue.popleft()\n", + "\n", + " # Check if the current node's value is a duplicate\n", + " if node.val in seen:\n", + " return node.val # Return the first duplicate value found\n", + "\n", + " seen.add(node.val) # Mark the current node's value as seen\n", + "\n", + " # Add children to the queue for further exploration\n", + " if node.left:\n", + " queue.append(node.left)\n", + " if node.right:\n", + " queue.append(node.right)\n", + "\n", + " return -1 # If no duplicate is found" ] }, { @@ -248,7 +346,16 @@ "metadata": {}, "outputs": [], "source": [ - "# Your answer here" + "# Breadth-first traversal ensures that any duplicate value \n", + "# closer to the root is found first because it explores nodes layer-by-layer.\n", + "\n", + "# The approach is fast and efficient: The seen set keeps track of all node values encountered during the traversal.\n", + "# Checking if a value is in the set is very fast and adding a value to the set is also fast\n", + "# ensuring that duplicates can be detected efficiently.\n", + "\n", + "# As soon as the function detects that a node's value is already in the seen set, it immediately returns \n", + "# that value as the first duplicate. Ensuring that the solution is optimal and stops as soon as the problem is solved, \n", + "# without traversing unnecessary parts of the tree.\n" ] }, { @@ -265,7 +372,19 @@ "metadata": {}, "outputs": [], "source": [ - "# Your answer here" + "# Time Complexity: 𝑂(𝑛) where 𝑛 is the number of nodes in the tree\n", + "# Breadth-First Traversal:\n", + "# - Each node in the binary tree is visited exactly once during the traversal. \n", + "# - If there are 𝑛 nodes in the tree, the traversal takes 𝑂(𝑛) time.\n", + "# Set Operations:\n", + "# - Checking whether a value is in the seen set and adding a value to the set both take average 𝑂(1) time.\n", + "# - These operations are performed once for each node.\n", + "\n", + "# Space Complexity: 𝑂(𝑛)\n", + "# Queue (for Level-Order Traversal):\n", + "# - The queue stores nodes at the current level during traversal.\n", + "# Set (for Tracking Seen Values): The seen set stores the values of all visited nodes.\n", + "# - In the worst case (when no duplicates exist), the set will contain all 𝑛 node values." ] }, { @@ -282,7 +401,16 @@ "metadata": {}, "outputs": [], "source": [ - "# Your answer here" + "# An alternative to the breadth-first traversal is to use a pre-order depth-first traversal \n", + "# The tree is explored by checking the current node first, then the left subtree, and finally the right subtree.\n", + "# - specifically, depth-first explores one branch fully before moving to the next.\n", + "# The seen set keeps track of node values found during traversal and if the value is already in the set, \n", + "# it is returned as the first duplicate.\n", + "# If a duplicate is found in the left or right subtree, the function stops and sends that result back right away\n", + "\n", + "# Unlike breadth-first (which ensures the first duplicate closest to the root is detected first), \n", + "# depth-first finds the first duplicate based on its traversal order (in this case pre-order), \n", + "# which might not always be the closest to the root." ] }, { @@ -330,7 +458,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "dsi_participant", "language": "python", "name": "python3" }, @@ -344,7 +472,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.7" + "version": "3.9.15" } }, "nbformat": 4,