diff --git a/binary-tree-level-order-traversal/dolphinflow86.py b/binary-tree-level-order-traversal/dolphinflow86.py new file mode 100644 index 0000000000..522a2089e1 --- /dev/null +++ b/binary-tree-level-order-traversal/dolphinflow86.py @@ -0,0 +1,37 @@ +# N is the total number of nodes in the binary tree. +# TC: O(N) - each node is visited and enqueued/dequeued once +# SC: O(N) - queue stores at most the maximum width of the tree (up to N / 2 nodes) + +from collections import deque +from typing import List, Optional + + +# 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 levelOrder(self, root: Optional["TreeNode"]) -> List[List[int]]: + if not root: + return [] + + levels = [] + queue = deque([root]) + + while queue: + level_vals = [] + for _ in range(len(queue)): + node = queue.popleft() + level_vals.append(node.val) + if node.left: + queue.append(node.left) + if node.right: + queue.append(node.right) + levels.append(level_vals) + + return levels diff --git a/counting-bits/dolphinflow86.py b/counting-bits/dolphinflow86.py new file mode 100644 index 0000000000..d9c7004e80 --- /dev/null +++ b/counting-bits/dolphinflow86.py @@ -0,0 +1,14 @@ +# N is the given integer. +# TC: O(N) - single pass from 0 to N using previous subproblem results +# SC: O(1) - auxiliary space (O(N) to store the result array) + +from typing import List + + +class Solution: + + def countBits(self, n: int) -> List[int]: + ans = [0] * (n + 1) + for i in range(1, n + 1): + ans[i] = ans[i >> 1] + (i & 1) + return ans diff --git a/house-robber-ii/dolphinflow86.py b/house-robber-ii/dolphinflow86.py new file mode 100644 index 0000000000..df67a21998 --- /dev/null +++ b/house-robber-ii/dolphinflow86.py @@ -0,0 +1,22 @@ +# N is the number of houses. +# TC: O(N) - two passes of linear house robber (excluding first or last house) +# SC: O(1) - constant auxiliary space tracking two variables for DP + +from typing import List + + +class Solution: + + def rob(self, nums: List[int]) -> int: + if not nums: + return 0 + if len(nums) <= 2: + return max(nums) + + def rob_linear(houses: List[int]) -> int: + prev2, prev1 = 0, 0 + for money in houses: + prev2, prev1 = prev1, max(prev1, prev2 + money) + return prev1 + + return max(rob_linear(nums[:-1]), rob_linear(nums[1:])) diff --git a/meeting-rooms-ii/dolphinflow86.py b/meeting-rooms-ii/dolphinflow86.py new file mode 100644 index 0000000000..e621b40957 --- /dev/null +++ b/meeting-rooms-ii/dolphinflow86.py @@ -0,0 +1,32 @@ +# N is the number of meeting intervals. +# TC: O(N log N) - sorting intervals by start time and maintaining a min-heap +# SC: O(N) - heap stores at most N end times in the worst case + +import heapq +from typing import Any, List + + +class Solution: + + def minMeetingRooms(self, intervals: List[Any]) -> int: + if not intervals: + return 0 + + # Support both List[List[int]] and List[Interval] + def get_interval(item): + if hasattr(item, "start") and hasattr(item, "end"): + return item.start, item.end + return item[0], item[1] + + normalized = [get_interval(interval) for interval in intervals] + normalized.sort(key=lambda x: x[0]) + + min_heap = [] + for start, end in normalized: + if min_heap and min_heap[0] <= start: + heapq.heappop(min_heap) + heapq.heappush(min_heap, end) + + return len(min_heap) + + min_meeting_rooms = minMeetingRooms diff --git a/word-search-ii/dolphinflow86.py b/word-search-ii/dolphinflow86.py new file mode 100644 index 0000000000..0895c64e59 --- /dev/null +++ b/word-search-ii/dolphinflow86.py @@ -0,0 +1,59 @@ +# M, N are dimensions of the board, W is the number of words, and L is the maximum length of a word. +# TC: O(W * L + M * N * 3^(L - 1)) - building Trie takes O(W * L); backtracking explores at most 3 directions after first step up to depth L +# SC: O(W * L) - Trie storage for all words and recursion stack depth up to L + +from typing import List + + +class TrieNode: + + def __init__(self): + self.children = {} + self.word = None + + +class Solution: + + def findWords(self, board: List[List[str]], words: List[str]) -> List[str]: + root = TrieNode() + for word in words: + node = root + for char in word: + if char not in node.children: + node.children[char] = TrieNode() + node = node.children[char] + node.word = word + + rows, cols = len(board), len(board[0]) + result = [] + + def dfs(r: int, c: int, parent: TrieNode): + char = board[r][c] + curr_node = parent.children.get(char) + if not curr_node: + return + + if curr_node.word: + result.append(curr_node.word) + curr_node.word = None + + board[r][c] = "#" # mark visited + + for dr, dc in ((-1, 0), (1, 0), (0, -1), (0, 1)): + nr, nc = r + dr, c + dc + if 0 <= nr < rows and 0 <= nc < cols and board[nr][nc] != "#": + if board[nr][nc] in curr_node.children: + dfs(nr, nc, curr_node) + + board[r][c] = char # backtrack + + # Prune leaf nodes to accelerate search + if not curr_node.children: + del parent.children[char] + + for r in range(rows): + for c in range(cols): + if board[r][c] in root.children: + dfs(r, c, root) + + return result