diff --git a/find-median-from-data-stream/yolophg.py b/find-median-from-data-stream/yolophg.py new file mode 100644 index 000000000..f158591e8 --- /dev/null +++ b/find-median-from-data-stream/yolophg.py @@ -0,0 +1,31 @@ +# Time Complexity: +# - addNum(): O(log N) - use a heap operation (push/pop) which takes log N time. +# - findMedian(): O(1) - just accessing the top elements of heaps. +# Space Complexity: O(N) - store all elements in two heaps. + + +class MedianFinder: + def __init__(self): + # max heap (stores the smaller half of numbers, negated values for max heap behavior) + self.maxHeap = [] + # min heap (stores the larger half of numbers) + self.minHeap = [] + + def addNum(self, num: int) -> None: + if not self.maxHeap or num <= -self.maxHeap[0]: + # store as negative to simulate max heap + heapq.heappush(self.maxHeap, -num) + else: + heapq.heappush(self.minHeap, num) + + # balance the heaps: maxHeap can have at most 1 more element than minHeap + if len(self.maxHeap) > len(self.minHeap) + 1: + heapq.heappush(self.minHeap, -heapq.heappop(self.maxHeap)) + elif len(self.minHeap) > len(self.maxHeap): + heapq.heappush(self.maxHeap, -heapq.heappop(self.minHeap)) + + def findMedian(self) -> float: + if len(self.maxHeap) > len(self.minHeap): + return -self.maxHeap[0] # odd number of elements, maxHeap has the median + else: + return (-self.maxHeap[0] + self.minHeap[0]) / 2.0 # even case, average of two middle numbers diff --git a/insert-interval/yolophg.py b/insert-interval/yolophg.py new file mode 100644 index 000000000..27dec9ce1 --- /dev/null +++ b/insert-interval/yolophg.py @@ -0,0 +1,26 @@ + # Time Complexity: O(N) - iterate through the intervals once. +# Space Complexity: O(N) - store the merged intervals in a new list. + +class Solution: + def insert(self, intervals: List[List[int]], newInterval: List[int]) -> List[List[int]]: + output = [] + start, end = newInterval + + for interval in intervals: + if interval[1] < start: + # no overlap, and the current interval ends before newInterval starts + output.append(interval) + elif interval[0] > end: + # no overlap, and the current interval starts after newInterval ends + output.append([start, end]) # insert merged interval before appending the remaining ones + output.extend(intervals[intervals.index(interval):]) # append remaining intervals + return output + else: + # overlapping case: merge intervals + start = min(start, interval[0]) + end = max(end, interval[1]) + + # add the merged interval at the end if it wasn't added earlier + output.append([start, end]) + + return output diff --git a/kth-smallest-element-in-a-bst/yolophg.py b/kth-smallest-element-in-a-bst/yolophg.py new file mode 100644 index 000000000..e90a106ab --- /dev/null +++ b/kth-smallest-element-in-a-bst/yolophg.py @@ -0,0 +1,21 @@ +# Time Complexity: O(N) - visit each node once in an inorder traversal. +# Space Complexity: O(N) - store all node values in a list. + +class Solution: + def kthSmallest(self, root: Optional[TreeNode], k: int) -> int: + def inorder(node): + if not node: + return + + # go left (smaller values first) + inorder(node.left) + # add the current node's value + values.append(node.val) + # go right (larger values next) + inorder(node.right) + + values = [] + inorder(root) + + # get the k-th smallest element (1-based index) + return values[k - 1] diff --git a/lowest-common-ancestor-of-a-binary-search-tree/yolophg.py b/lowest-common-ancestor-of-a-binary-search-tree/yolophg.py new file mode 100644 index 000000000..93cc6907b --- /dev/null +++ b/lowest-common-ancestor-of-a-binary-search-tree/yolophg.py @@ -0,0 +1,21 @@ +# Time Complexity: O(log N) on average (for balanced BST), worst case O(N) (skewed tree). +# Space Complexity: O(1) - don't use extra space, just a few variables. + +class Solution: + def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode': + # start from the root + node = root + + while node: + if p.val > node.val and q.val > node.val: + # both p and q are in the right subtree, so move right + node = node.right + elif p.val < node.val and q.val < node.val: + # both p and q are in the left subtree, so move left + node = node.left + else: + # found the split point where one is on the left and the other is on the right + # or when we reach p or q directly (since a node can be its own ancestor) + return node + + return None diff --git a/meeting-rooms/yolophg.py b/meeting-rooms/yolophg.py new file mode 100644 index 000000000..f46f585cf --- /dev/null +++ b/meeting-rooms/yolophg.py @@ -0,0 +1,19 @@ +# Time Complexity: O(N log N) - sorting the intervals takes O(N log N), and the iteration takes O(N). +# Space Complexity: O(1) - sorting is done in place, and we use only a few extra variables. + +class Solution: + def can_attend_meetings(intervals): + if not intervals: + return True + + # sort intervals based on start times + intervals.sort() # O(N log N) sorting + + # check for overlapping meetings + for i in range(len(intervals) - 1): + if intervals[i][1] > intervals[i + 1][0]: + # if the current meeting's end time is later than the next meeting's start time, have a conflict + return False + + # no conflicts found, all meetings can be attended + return True