From bfd2f63a43f4b960a27d021bc4b797be77fb91b3 Mon Sep 17 00:00:00 2001 From: Sanket Kale Date: Mon, 29 Jun 2026 21:48:48 -0500 Subject: [PATCH] Completed BFS-1 --- Problem1-DFS.py | 34 +++++++++++++++++++++++++ Problem1.py | 49 +++++++++++++++++++++++++++++++++++ Problem2.py | 68 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 151 insertions(+) create mode 100644 Problem1-DFS.py create mode 100644 Problem1.py create mode 100644 Problem2.py diff --git a/Problem1-DFS.py b/Problem1-DFS.py new file mode 100644 index 00000000..de6878f9 --- /dev/null +++ b/Problem1-DFS.py @@ -0,0 +1,34 @@ +class Solution: + def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]: + # Time Complexity: O(N), where N is the number of nodes in the tree. + # Each node is visited exactly once during the traversal. + # Space Complexity: O(N) overall. The recursion stack uses O(H) space, where H is the + # tree height (worst case O(N) for a skewed tree, O(log N) for balanced). + # The 'result' array also takes O(N) space to store the node values. + + result = [] + + # Logic: Use a recursive helper function to perform DFS, keeping track of the current depth 'level'. + def dfs(root, level): + nonlocal result + + # Base case: if the current node is null, stop recursion. + if not root: + return + + # Logic: If the length of 'result' equals the current 'level', it means we are + # visiting this depth for the first time. We append a new list with the node's value. + if len(result) == level: + result.append([root.val]) + # Logic: If a list for this depth already exists, simply append the node's value to it. + else: + result[level].append(root.val) + + # Recursively traverse the left and right subtrees, incrementing the level by 1. + dfs(root.left, level + 1) + dfs(root.right, level + 1) + + # Initialize the DFS traversal starting at the root node (level 0). + dfs(root, 0) + + return result \ No newline at end of file diff --git a/Problem1.py b/Problem1.py new file mode 100644 index 00000000..c1761e70 --- /dev/null +++ b/Problem1.py @@ -0,0 +1,49 @@ +# Problem 1 +# Binary Tree Level Order Traversal (https://leetcode.com/problems/binary-tree-level-order-traversal/) + +#BFS +class Solution: + def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]: + # Time Complexity: O(N) where N is the number of nodes in the tree. + # Each node is visited and popped from the queue exactly once. + # Space Complexity: O(N) since the queue will hold up to N/2 nodes at the bottom + # level of a balanced binary tree. The 'result' array also takes O(N) space. + + # Logic: Handle the edge case of an empty tree. + if not root: + return [] + + # Logic: Use a double-ended queue (deque) to perform a Breadth-First Search (BFS). + q = collections.deque() + q.append(root) + + # 'size' dictates how many nodes need to be processed for the current level. + size = 1 + result = [] + level = [] + + # Logic: Traverse the tree level by level as long as the queue isn't empty. + while q: + # Iterate through all the nodes present in the current level. + for i in range(size): + element = q.popleft() + + # If children exist, append them to the queue for the next level. + if element.left: + q.append(element.left) + if element.right: + q.append(element.right) + + # Store the current node's value in the current level's list. + level.append(element.val) + + # After finishing the current level, add it to our final result array. + result.append(level) + + # Clear the level array to prepare for the next level's values. + level = [] + + # Update 'size' to match the number of children added for the next level. + size = len(q) + + return result \ No newline at end of file diff --git a/Problem2.py b/Problem2.py new file mode 100644 index 00000000..6788b675 --- /dev/null +++ b/Problem2.py @@ -0,0 +1,68 @@ +# Problem 2 +# Course Schedule (https://leetcode.com/problems/course-schedule/) +import collections +from collections import defaultdict +from typing import List + +class Solution: + def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool: + """ + Determines if all courses can be finished using Kahn's Algorithm (Topological Sort). + + Time Complexity: O(V + E) where V is numCourses and E is the number of prerequisites. + Space Complexity: O(V + E) to store the adjacency list, indegrees array, and queue. + """ + + # SPACE: O(V) - Tracks how many prerequisites are required for each course. + indegrees = [0 for _ in range(numCourses)] + + # SPACE: O(V + E) - Adjacency list mapping a course to the courses that depend on it. + d = defaultdict(list) + + # TIME: O(E) - Build the graph and populate the indegrees array. + for [dep, req] in prerequisites: + indegrees[dep] += 1 + d[req].append(dep) + + # SPACE: O(V) - Queue to process courses that have zero unmet prerequisites. + q = collections.deque() + + # TIME: O(V) - Find all courses with no prerequisites and add them to the queue. + for i in range(numCourses): + if indegrees[i] == 0: + q.append(i) + + # Early exit: If all courses have 0 prerequisites, we can finish them all immediately. + if len(q) == numCourses: + return True + + # Early exit: If no courses have 0 prerequisites, there's a circular dependency involving all courses. + if not len(q): + return False + + # Track how many courses we successfully "take" (process). + processedCourses = 0 + + # TIME: O(V + E) - BFS traversal + while len(q): + # "Take" the course with 0 prerequisites + course = q.popleft() + processedCourses += 1 + + dependents = d[course] + + # Reduce the indegree for all courses that depended on the one we just took + for i in range(len(dependents)): + dependent = dependents[i] + indegrees[dependent] -= 1 + + # If a dependent course now has no remaining prerequisites, queue it up + if indegrees[dependent] == 0: + q.append(dependent) + + # If the number of courses we processed equals the total number of courses, + # it means there were no cycles and we could finish everything. + if processedCourses == numCourses: + return True + + return False \ No newline at end of file