diff --git a/Problem1.py b/Problem1.py new file mode 100644 index 00000000..11a065cb --- /dev/null +++ b/Problem1.py @@ -0,0 +1,37 @@ +# Time Complexity : O(log n) # Binary search halves the search space each step +# Space Complexity : O(1) # Only a few variables used +# Did this code successfully run on Leetcode : Yes +# Approach: +# 1. If first element is not 1 or last element is not n+1, handle those boundary cases directly. +# 2. Otherwise, use binary search: if arr[mid] == mid+1 → left side is correct, move right. +# 3. Else, move left. At the end, the missing number is (lo + 1). + +def missingNumber(arr): + n = len(arr) + + # Handle edge case: if the very first element is not 1, then 1 is missing + if arr[0] != 1: + return 1 + + # Handle edge case: if the last element is not n+1, then the missing number is at the end + if arr[n - 1] != n + 1: + return n + 1 + + # Binary search setup + lo, hi = 0, n - 1 + + while lo <= hi: + mid = (lo + hi) // 2 # Find middle index + + if arr[mid] == mid + 1: + # If value matches expected (index+1), left side is correct + # Missing number must be on the right + lo = mid + 1 + else: + # Otherwise mismatch happened here or earlier + # Move search to the left side + hi = mid - 1 + + # At the end, 'lo' will stop at the index where mismatch begins + # Missing number is index + 1 because numbers are 1-based + return lo + 1 diff --git a/Problem2.py b/Problem2.py new file mode 100644 index 00000000..38e5b916 --- /dev/null +++ b/Problem2.py @@ -0,0 +1,71 @@ +# Time Complexity : +# - Insert: O(log n) because we bubble up at most the height of the heap +# - Extract Min: O(log n) because we bubble down at most the height of the heap +# - Get Min: O(1) because root is always min +# Space Complexity : O(n) for storing heap elements +# Did this code successfully run on Leetcode : (Custom structure, but logic matches Leetcode heaps) +# Approach: +# 1. Represent the heap as an array where parent/child relations are derived from indices. +# 2. Insert by appending to the end and bubbling up until parent is smaller. +# 3. Extract min by swapping root with last element, removing it, then bubbling down. + +class MinHeap: + def __init__(self): + self.heap = [] # underlying array to store heap + + # Get parent index + def parent(self, i): + return (i - 1) // 2 + + # Get left child index + def left(self, i): + return 2 * i + 1 + + # Get right child index + def right(self, i): + return 2 * i + 2 + + # Insert a new key + def insert(self, key): + self.heap.append(key) # Step 1: add at the end + i = len(self.heap) - 1 + # Step 2: bubble up while parent is bigger + while i > 0 and self.heap[self.parent(i)] > self.heap[i]: + self.heap[i], self.heap[self.parent(i)] = self.heap[self.parent(i)], self.heap[i] + i = self.parent(i) + + # Heapify (bubble down) from index i + def heapify(self, i): + l = self.left(i) + r = self.right(i) + smallest = i + + # Compare with left child + if l < len(self.heap) and self.heap[l] < self.heap[smallest]: + smallest = l + # Compare with right child + if r < len(self.heap) and self.heap[r] < self.heap[smallest]: + smallest = r + + # If child is smaller, swap and continue + if smallest != i: + self.heap[i], self.heap[smallest] = self.heap[smallest], self.heap[i] + self.heapify(smallest) + + # Extract the minimum element (root) + def extractMin(self): + if not self.heap: + return None + if len(self.heap) == 1: + return self.heap.pop() + + # Step 1: Swap root with last element + root = self.heap[0] + self.heap[0] = self.heap.pop() + # Step 2: Heapify down from root + self.heapify(0) + return root + + # Get the minimum element without removing + def getMin(self): + return self.heap[0] if self.heap else None