Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions Problem1-DFS.py
Original file line number Diff line number Diff line change
@@ -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
49 changes: 49 additions & 0 deletions Problem1.py
Original file line number Diff line number Diff line change
@@ -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
68 changes: 68 additions & 0 deletions Problem2.py
Original file line number Diff line number Diff line change
@@ -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