diff --git a/find-median-from-data-stream/dolphinflow86.py b/find-median-from-data-stream/dolphinflow86.py new file mode 100644 index 0000000000..f253ccf04c --- /dev/null +++ b/find-median-from-data-stream/dolphinflow86.py @@ -0,0 +1,32 @@ +# N is the number of elements added to the data stream. +# TC: O(log N) for addNum, O(1) for findMedian +# SC: O(N) - stores elements split into two heaps + +import heapq + + +class MedianFinder: + + def __init__(self): + self.small = [] # max-heap (store negative values) + self.large = [] # min-heap + + def addNum(self, num: int) -> None: + heapq.heappush(self.small, -num) + + if self.small and self.large and (-self.small[0] > self.large[0]): + val = -heapq.heappop(self.small) + heapq.heappush(self.large, val) + + if len(self.small) > len(self.large) + 1: + val = -heapq.heappop(self.small) + heapq.heappush(self.large, val) + + if len(self.large) > len(self.small): + val = heapq.heappop(self.large) + heapq.heappush(self.small, -val) + + def findMedian(self) -> float: + if len(self.small) > len(self.large): + return float(-self.small[0]) + return (-self.small[0] + self.large[0]) / 2.0 diff --git a/insert-interval/dolphinflow86.py b/insert-interval/dolphinflow86.py new file mode 100644 index 0000000000..bf88f0cf56 --- /dev/null +++ b/insert-interval/dolphinflow86.py @@ -0,0 +1,28 @@ +# N is the number of intervals. +# TC: O(N) - single pass through intervals array +# SC: O(N) - stores result intervals list + + +class Solution: + + def insert(self, intervals, newInterval): + result = [] + i = 0 + n = len(intervals) + + while i < n and intervals[i][1] < newInterval[0]: + result.append(intervals[i]) + i += 1 + + while i < n and intervals[i][0] <= newInterval[1]: + newInterval[0] = min(newInterval[0], intervals[i][0]) + newInterval[1] = max(newInterval[1], intervals[i][1]) + i += 1 + + result.append(newInterval) + + while i < n: + result.append(intervals[i]) + i += 1 + + return result diff --git a/kth-smallest-element-in-a-bst/dolphinflow86.py b/kth-smallest-element-in-a-bst/dolphinflow86.py new file mode 100644 index 0000000000..5ca516b32b --- /dev/null +++ b/kth-smallest-element-in-a-bst/dolphinflow86.py @@ -0,0 +1,32 @@ +# H is the height of the BST, and K is the target rank. +# TC: O(H + K) - in-order traversal stops after visiting K elements +# SC: O(H) - stack memory for in-order traversal recursion/loop + +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + + +class Solution: + + def kthSmallest(self, root, k: int) -> int: + stack = [] + curr = root + + while curr or stack: + while curr: + stack.append(curr) + curr = curr.left + + curr = stack.pop() + k -= 1 + + if k == 0: + return curr.val + + curr = curr.right + + return -1 diff --git a/lowest-common-ancestor-of-a-binary-search-tree/dolphinflow86.py b/lowest-common-ancestor-of-a-binary-search-tree/dolphinflow86.py new file mode 100644 index 0000000000..5093ce94b2 --- /dev/null +++ b/lowest-common-ancestor-of-a-binary-search-tree/dolphinflow86.py @@ -0,0 +1,26 @@ +# H is the height of the binary search tree. +# TC: O(H) - traverses down tree height +# SC: O(1) - uses iterative traversal without recursion stack + +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + + +class Solution: + + def lowestCommonAncestor(self, root, p, q): + curr = root + + while curr: + if p.val < curr.val and q.val < curr.val: + curr = curr.left + elif p.val > curr.val and q.val > curr.val: + curr = curr.right + else: + return curr + + return None diff --git a/number-of-connected-components-in-an-undirected-graph/dolphinflow86.py b/number-of-connected-components-in-an-undirected-graph/dolphinflow86.py new file mode 100644 index 0000000000..bbb84db20c --- /dev/null +++ b/number-of-connected-components-in-an-undirected-graph/dolphinflow86.py @@ -0,0 +1,34 @@ +# n is the number of nodes, and E is the number of edges. +# TC: O(V + E * alpha(V)) - near linear time with path compression Union-Find +# SC: O(V) - parent and rank arrays for Union-Find + + +class Solution: + + def countComponents(self, n: int, edges) -> int: + parent = list(range(n)) + rank = [1] * n + + def find(node): + if parent[node] != node: + parent[node] = find(parent[node]) + return parent[node] + + def union(n1, n2): + p1, p2 = find(n1), find(n2) + if p1 == p2: + return 0 + + if rank[p1] > rank[p2]: + parent[p2] = p1 + rank[p1] += rank[p2] + else: + parent[p1] = p2 + rank[p2] += rank[p1] + return 1 + + components = n + for u, v in edges: + components -= union(u, v) + + return components diff --git a/serialize-and-deserialize-binary-tree/dolphinflow86.py b/serialize-and-deserialize-binary-tree/dolphinflow86.py new file mode 100644 index 0000000000..96e1fbe4c7 --- /dev/null +++ b/serialize-and-deserialize-binary-tree/dolphinflow86.py @@ -0,0 +1,44 @@ +# N is the number of nodes in the binary tree. +# TC: O(N) - visits each node once during serialization and deserialization +# SC: O(N) - stores node values and recursion stack + +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + + +class Codec: + + def serialize(self, root): + vals = [] + + def dfs(node): + if not node: + vals.append("N") + return + vals.append(str(node.val)) + dfs(node.left) + dfs(node.right) + + dfs(root) + return ",".join(vals) + + def deserialize(self, data): + vals = data.split(",") + self.i = 0 + + def dfs(): + if vals[self.i] == "N": + self.i += 1 + return None + + node = TreeNode(int(vals[self.i])) + self.i += 1 + node.left = dfs() + node.right = dfs() + return node + + return dfs()