diff --git a/azure-aks-python-app/infrastructure/scripts/setup-dns-dsa-hugecat.sh b/azure-aks-python-app/infrastructure/scripts/setup-dns-dsa-hugecat.sh index 98011fb..00b16b9 100755 --- a/azure-aks-python-app/infrastructure/scripts/setup-dns-dsa-hugecat.sh +++ b/azure-aks-python-app/infrastructure/scripts/setup-dns-dsa-hugecat.sh @@ -25,13 +25,13 @@ echo "Gateway IP: $GATEWAY_IP" if [ -z "$RESOURCE_GROUP" ]; then echo "Finding resource group for DNS zone $DNS_ZONE..." RESOURCE_GROUP=$(az network dns zone list --query "[?name=='$DNS_ZONE'].resourceGroup | [0]" -o tsv) - + if [ -z "$RESOURCE_GROUP" ]; then echo "ERROR: Could not find resource group for DNS zone $DNS_ZONE" echo "Please set RESOURCE_GROUP environment variable or create the DNS zone first." exit 1 fi - + echo "Found resource group: $RESOURCE_GROUP" fi diff --git a/src/interview_workbook/leetcode/backtracking/combination_sum.py b/src/interview_workbook/leetcode/backtracking/combination_sum.py index c9b8b7d..06bbccf 100644 --- a/src/interview_workbook/leetcode/backtracking/combination_sum.py +++ b/src/interview_workbook/leetcode/backtracking/combination_sum.py @@ -12,6 +12,10 @@ from typing import List +from src.interview_workbook.leetcode._registry import register_problem +from src.interview_workbook.leetcode._runner import TestCase +from src.interview_workbook.leetcode._types import Category, Difficulty + class Solution: def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: @@ -29,11 +33,51 @@ def backtrack(start: int, path: List[int], total: int): path.pop() backtrack(0, [], 0) - print(f"All combinations: {res}") return res +# Example test cases +test_cases = [ + TestCase(([2, 3, 6, 7], 7), [[2, 2, 3], [7]], "Standard case"), + TestCase(([2, 3, 5], 8), [[2, 2, 2, 2], [2, 3, 3], [3, 5]], "Multiple solutions"), + TestCase(([2], 1), [], "No solution possible"), +] + + def demo() -> str: - s = Solution() - result = s.combinationSum([2, 3, 6, 7], 7) - return str(result) + """Run test cases for Combination Sum.""" + sol = Solution() + outputs = [] + outputs.append("Combination Sum | LeetCode 39") + outputs.append("=" * 50) + outputs.append("Time: O(2^target/min) | Space: O(target/min)") + outputs.append("Technique: Backtracking with pruning\n") + + for case in test_cases: + res = sol.combinationSum(*case.input_args) + # Sort for comparison since order may vary + res_sorted = sorted([sorted(x) for x in res]) + expected_sorted = sorted([sorted(x) for x in case.expected]) + passed = res_sorted == expected_sorted + status = "✓ PASS" if passed else "✗ FAIL" + outputs.append(f"Test Case: {case.description}") + outputs.append(f" Input: candidates={case.input_args[0]}, target={case.input_args[1]}") + outputs.append(f" Output: {res}") + outputs.append(f" Expected: {case.expected}") + outputs.append(f" {status}\n") + + result = "\n".join(outputs) + print(result) + return result + + +register_problem( + id=39, + slug="combination_sum", + title="Combination Sum", + category=Category.BACKTRACKING, + difficulty=Difficulty.MEDIUM, + tags=["array", "backtracking"], + url="https://leetcode.com/problems/combination-sum/", + notes="", +) diff --git a/src/interview_workbook/leetcode/backtracking/combination_sum_ii.py b/src/interview_workbook/leetcode/backtracking/combination_sum_ii.py index 81b72f3..70a0a47 100644 --- a/src/interview_workbook/leetcode/backtracking/combination_sum_ii.py +++ b/src/interview_workbook/leetcode/backtracking/combination_sum_ii.py @@ -11,6 +11,7 @@ from typing import List from src.interview_workbook.leetcode._registry import register_problem +from src.interview_workbook.leetcode._runner import TestCase from src.interview_workbook.leetcode._types import Category, Difficulty @@ -36,11 +37,43 @@ def backtrack(start, path, remain): return res -def demo(): - s = Solution() - result = s.combinationSum2([10, 1, 2, 7, 6, 1, 5], 8) - print(f"Final result: {result}") - return str(result) +# Example test cases +test_cases = [ + TestCase( + ([10, 1, 2, 7, 6, 1, 5], 8), + [[1, 1, 6], [1, 2, 5], [1, 7], [2, 6]], + "Standard case with duplicates", + ), + TestCase(([2, 5, 2, 1, 2], 5), [[1, 2, 2], [5]], "Multiple ways to reach target"), + TestCase(([1, 1, 1, 1], 2), [[1, 1]], "All same elements"), +] + + +def demo() -> str: + """Run test cases for Combination Sum II.""" + sol = Solution() + outputs = [] + outputs.append("Combination Sum II | LeetCode 40") + outputs.append("=" * 50) + outputs.append("Time: O(2^n) | Space: O(n)") + outputs.append("Technique: Backtracking with duplicate skipping\n") + + for case in test_cases: + res = sol.combinationSum2(*case.input_args) + # Sort for comparison since order may vary + res_sorted = sorted([sorted(x) for x in res]) + expected_sorted = sorted([sorted(x) for x in case.expected]) + passed = res_sorted == expected_sorted + status = "✓ PASS" if passed else "✗ FAIL" + outputs.append(f"Test Case: {case.description}") + outputs.append(f" Input: candidates={case.input_args[0]}, target={case.input_args[1]}") + outputs.append(f" Output: {res}") + outputs.append(f" Expected: {case.expected}") + outputs.append(f" {status}\n") + + result = "\n".join(outputs) + print(result) + return result register_problem( diff --git a/src/interview_workbook/leetcode/backtracking/permutations.py b/src/interview_workbook/leetcode/backtracking/permutations.py index 9cf32e3..50ff457 100644 --- a/src/interview_workbook/leetcode/backtracking/permutations.py +++ b/src/interview_workbook/leetcode/backtracking/permutations.py @@ -7,6 +7,7 @@ from typing import List from src.interview_workbook.leetcode._registry import register_problem +from src.interview_workbook.leetcode._runner import TestCase from src.interview_workbook.leetcode._types import Category, Difficulty @@ -27,11 +28,43 @@ def backtrack(start=0): return res -def demo(): - s = Solution() - result = s.permute([1, 2, 3]) - print(f"Final result: {result}") - return str(result) +# Example test cases +test_cases = [ + TestCase( + ([1, 2, 3],), + [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]], + "Three elements", + ), + TestCase(([0, 1],), [[0, 1], [1, 0]], "Two elements"), + TestCase(([1],), [[1]], "Single element"), +] + + +def demo() -> str: + """Run test cases for Permutations.""" + sol = Solution() + outputs = [] + outputs.append("Permutations | LeetCode 46") + outputs.append("=" * 50) + outputs.append("Time: O(n! * n) | Space: O(n)") + outputs.append("Technique: Backtracking with in-place swapping\n") + + for case in test_cases: + res = sol.permute(list(case.input_args[0])) # Copy to avoid mutation + # Sort for comparison since order may vary + res_sorted = sorted([tuple(x) for x in res]) + expected_sorted = sorted([tuple(x) for x in case.expected]) + passed = res_sorted == expected_sorted + status = "✓ PASS" if passed else "✗ FAIL" + outputs.append(f"Test Case: {case.description}") + outputs.append(f" Input: nums={list(case.input_args[0])}") + outputs.append(f" Output: {res}") + outputs.append(f" Expected: {case.expected}") + outputs.append(f" {status}\n") + + result = "\n".join(outputs) + print(result) + return result register_problem( diff --git a/src/interview_workbook/leetcode/backtracking/subsets.py b/src/interview_workbook/leetcode/backtracking/subsets.py index 554395e..721d676 100644 --- a/src/interview_workbook/leetcode/backtracking/subsets.py +++ b/src/interview_workbook/leetcode/backtracking/subsets.py @@ -9,6 +9,7 @@ from typing import List from src.interview_workbook.leetcode._registry import register_problem +from src.interview_workbook.leetcode._runner import TestCase from src.interview_workbook.leetcode._types import Category, Difficulty @@ -20,20 +21,49 @@ def backtrack(start: int, path: List[int]) -> None: res.append(path[:]) for i in range(start, len(nums)): path.append(nums[i]) - print(f"Path after append: {path}") backtrack(i + 1, path) path.pop() backtrack(0, []) - print(f"All subsets: {res}") return res -def demo(): - s = Solution() - result = s.subsets([1, 2, 3]) - print(f"Final result: {result}") - return str(result) +# Example test cases +test_cases = [ + TestCase( + ([1, 2, 3],), + [[], [1], [1, 2], [1, 2, 3], [1, 3], [2], [2, 3], [3]], + "Three elements", + ), + TestCase(([0],), [[], [0]], "Single element"), +] + + +def demo() -> str: + """Run test cases for Subsets.""" + sol = Solution() + outputs = [] + outputs.append("Subsets | LeetCode 78") + outputs.append("=" * 50) + outputs.append("Time: O(n * 2^n) | Space: O(n)") + outputs.append("Technique: Backtracking to generate power set\n") + + for case in test_cases: + res = sol.subsets(list(case.input_args[0])) + # Sort for comparison since order may vary + res_sorted = sorted([tuple(sorted(x)) for x in res]) + expected_sorted = sorted([tuple(sorted(x)) for x in case.expected]) + passed = res_sorted == expected_sorted + status = "✓ PASS" if passed else "✗ FAIL" + outputs.append(f"Test Case: {case.description}") + outputs.append(f" Input: nums={list(case.input_args[0])}") + outputs.append(f" Output: {res}") + outputs.append(f" Expected: {case.expected}") + outputs.append(f" {status}\n") + + result = "\n".join(outputs) + print(result) + return result register_problem( diff --git a/src/interview_workbook/leetcode/backtracking/subsets_ii.py b/src/interview_workbook/leetcode/backtracking/subsets_ii.py index d9fe227..9f944cd 100644 --- a/src/interview_workbook/leetcode/backtracking/subsets_ii.py +++ b/src/interview_workbook/leetcode/backtracking/subsets_ii.py @@ -8,6 +8,7 @@ from typing import List from src.interview_workbook.leetcode._registry import register_problem +from src.interview_workbook.leetcode._runner import TestCase from src.interview_workbook.leetcode._types import Category, Difficulty @@ -29,11 +30,58 @@ def backtrack(start: int, path: List[int]) -> None: return res +# Example test cases +test_cases = [ + TestCase( + ([1, 2, 2],), + [[], [1], [1, 2], [1, 2, 2], [2], [2, 2]], + "Array with duplicates", + ), + TestCase(([0],), [[], [0]], "Single element"), + TestCase( + ([4, 4, 4, 1, 4],), + [ + [], + [1], + [1, 4], + [1, 4, 4], + [1, 4, 4, 4], + [1, 4, 4, 4, 4], + [4], + [4, 4], + [4, 4, 4], + [4, 4, 4, 4], + ], + "Multiple duplicates", + ), +] + + def demo() -> str: - s = Solution() - result = s.subsetsWithDup([1, 2, 2]) - print(f"Final result: {result}") - return str(result) + """Run test cases for Subsets II.""" + sol = Solution() + outputs = [] + outputs.append("Subsets II | LeetCode 90") + outputs.append("=" * 50) + outputs.append("Time: O(n * 2^n) | Space: O(n)") + outputs.append("Technique: Backtracking with duplicate skipping\n") + + for case in test_cases: + res = sol.subsetsWithDup(list(case.input_args[0])) + # Sort for comparison since order may vary + res_sorted = sorted([tuple(sorted(x)) for x in res]) + expected_sorted = sorted([tuple(sorted(x)) for x in case.expected]) + passed = res_sorted == expected_sorted + status = "✓ PASS" if passed else "✗ FAIL" + outputs.append(f"Test Case: {case.description}") + outputs.append(f" Input: nums={list(case.input_args[0])}") + outputs.append(f" Output: {res}") + outputs.append(f" Expected: {case.expected}") + outputs.append(f" {status}\n") + + result = "\n".join(outputs) + print(result) + return result register_problem( diff --git a/src/interview_workbook/leetcode/binary_search/binary_search.py b/src/interview_workbook/leetcode/binary_search/binary_search.py index 9add724..a94371d 100644 --- a/src/interview_workbook/leetcode/binary_search/binary_search.py +++ b/src/interview_workbook/leetcode/binary_search/binary_search.py @@ -1,10 +1,13 @@ """ Binary Search -TODO: Add problem description +Problem: Binary Search +LeetCode link: https://leetcode.com/problems/binary-search/ +Description: Given a sorted array and a target, return the index of target if found, else -1. """ from src.interview_workbook.leetcode._registry import register_problem +from src.interview_workbook.leetcode._runner import TestCase class Solution: @@ -22,13 +25,39 @@ def search(self, nums: list[int], target: int) -> int: return -1 -def demo(): - """Run a demo for the Binary Search problem.""" - solver = Solution() - nums = [-1, 0, 3, 5, 9, 12] - target = 9 - result = solver.search(nums, target) - return str(result) +# Example test cases +test_cases = [ + TestCase(([-1, 0, 3, 5, 9, 12], 9), 4, "Target in middle"), + TestCase(([-1, 0, 3, 5, 9, 12], 2), -1, "Target not found"), + TestCase(([5], 5), 0, "Single element found"), + TestCase(([2, 5], 2), 0, "Target at start"), + TestCase(([2, 5], 5), 1, "Target at end"), +] + + +def demo() -> str: + """Run test cases for Binary Search.""" + sol = Solution() + outputs = [] + outputs.append("Binary Search | LeetCode 704") + outputs.append("=" * 50) + outputs.append("Time: O(log n) | Space: O(1)") + outputs.append("Technique: Classic binary search\n") + + for case in test_cases: + nums, target = case.input_args + res = sol.search(list(nums), target) + passed = res == case.expected + status = "✓ PASS" if passed else "✗ FAIL" + outputs.append(f"Test Case: {case.description}") + outputs.append(f" Input: nums={list(nums)}, target={target}") + outputs.append(f" Output: {res}") + outputs.append(f" Expected: {case.expected}") + outputs.append(f" {status}\n") + + result = "\n".join(outputs) + print(result) + return result # Register the problem with correct parameters diff --git a/src/interview_workbook/leetcode/binary_search/search_in_rotated_sorted_array.py b/src/interview_workbook/leetcode/binary_search/search_in_rotated_sorted_array.py index 9c13ab3..58d8917 100644 --- a/src/interview_workbook/leetcode/binary_search/search_in_rotated_sorted_array.py +++ b/src/interview_workbook/leetcode/binary_search/search_in_rotated_sorted_array.py @@ -7,6 +7,7 @@ """ from src.interview_workbook.leetcode._registry import register_problem +from src.interview_workbook.leetcode._runner import TestCase from src.interview_workbook.leetcode._types import Category, Difficulty @@ -31,13 +32,39 @@ def search(self, nums: list[int], target: int) -> int: return -1 -def demo(): - """Run a demo for the Search In Rotated Sorted Array problem.""" - solver = Solution() - nums = [4, 5, 6, 7, 0, 1, 2] - target = 0 - result = solver.search(nums, target) - return str(result) +# Example test cases +test_cases = [ + TestCase(([4, 5, 6, 7, 0, 1, 2], 0), 4, "Target in right half after rotation"), + TestCase(([4, 5, 6, 7, 0, 1, 2], 3), -1, "Target not found"), + TestCase(([1], 0), -1, "Single element not found"), + TestCase(([1], 1), 0, "Single element found"), + TestCase(([3, 1], 1), 1, "Two elements rotated"), +] + + +def demo() -> str: + """Run test cases for Search in Rotated Sorted Array.""" + sol = Solution() + outputs = [] + outputs.append("Search in Rotated Sorted Array | LeetCode 33") + outputs.append("=" * 50) + outputs.append("Time: O(log n) | Space: O(1)") + outputs.append("Technique: Modified binary search\n") + + for case in test_cases: + nums, target = case.input_args + res = sol.search(list(nums), target) + passed = res == case.expected + status = "✓ PASS" if passed else "✗ FAIL" + outputs.append(f"Test Case: {case.description}") + outputs.append(f" Input: nums={list(nums)}, target={target}") + outputs.append(f" Output: {res}") + outputs.append(f" Expected: {case.expected}") + outputs.append(f" {status}\n") + + result = "\n".join(outputs) + print(result) + return result register_problem( diff --git a/src/interview_workbook/leetcode/heap/find_median_from_data_stream.py b/src/interview_workbook/leetcode/heap/find_median_from_data_stream.py index 8204f04..90b1b95 100644 --- a/src/interview_workbook/leetcode/heap/find_median_from_data_stream.py +++ b/src/interview_workbook/leetcode/heap/find_median_from_data_stream.py @@ -1,7 +1,9 @@ """ Find Median From Data Stream -TODO: Add problem description +Problem: Find Median from Data Stream +LeetCode link: https://leetcode.com/problems/find-median-from-data-stream/ +Description: Design a data structure that supports adding numbers and finding the median in O(log n) time. """ import heapq @@ -39,24 +41,54 @@ def find_median(self) -> float: return float(-self.small[0]) return (-self.small[0] + self.large[0]) / 2.0 - def solve(self, nums: list[int]) -> float: - """Compute median by sequentially adding numbers from list.""" - for n in nums: - self.add_num(n) - return self.find_median() - def demo() -> str: - """Demo for Find Median From Data Stream.""" + """Run test cases for Find Median from Data Stream.""" + outputs = [] + outputs.append("Find Median from Data Stream | LeetCode 295") + outputs.append("=" * 50) + outputs.append("Time: O(log n) add, O(1) find | Space: O(n)") + outputs.append("Technique: Two heaps (max-heap for lower half, min-heap for upper)\n") + + # Test Case 1: Standard sequence + outputs.append("Test Case: Streaming median updates") + outputs.append(" Operations: addNum(1), addNum(2), findMedian(), addNum(3), findMedian()") + mf = Solution() + mf.add_num(1) + mf.add_num(2) + med1 = mf.find_median() + mf.add_num(3) + med2 = mf.find_median() + outputs.append(f" After [1,2] -> median={med1} (expected 1.5)") + outputs.append(f" After [1,2,3] -> median={med2} (expected 2.0)") + passed = med1 == 1.5 and med2 == 2.0 + outputs.append(f" {'✓ PASS' if passed else '✗ FAIL'}\n") + + # Test Case 2: All same numbers + outputs.append("Test Case: All same numbers") + mf2 = Solution() + for n in [5, 5, 5, 5]: + mf2.add_num(n) + med3 = mf2.find_median() + outputs.append(f" After [5,5,5,5] -> median={med3} (expected 5.0)") + passed2 = med3 == 5.0 + outputs.append(f" {'✓ PASS' if passed2 else '✗ FAIL'}\n") + + # Test Case 3: Larger sequence + outputs.append("Test Case: Larger stream") + mf3 = Solution() nums = [41, 35, 62, 4, 97, 108] - print(f"Initial stream: {nums}") - s = Solution() - for num in nums: - s.add_num(num) - print(f"Added {num}, current median: {s.find_median()}") - result = s.find_median() - print(f"Final median: {result}") - return f"Median of {nums} -> {result}" + for n in nums: + mf3.add_num(n) + med4 = mf3.find_median() + outputs.append(f" Stream: {nums}") + outputs.append(f" Final median: {med4} (expected 51.5)") + passed3 = med4 == 51.5 + outputs.append(f" {'✓ PASS' if passed3 else '✗ FAIL'}\n") + + result = "\n".join(outputs) + print(result) + return result register_problem( diff --git a/src/interview_workbook/leetcode/heap/kth_largest_element.py b/src/interview_workbook/leetcode/heap/kth_largest_element.py index 4ab9977..1ec34ea 100644 --- a/src/interview_workbook/leetcode/heap/kth_largest_element.py +++ b/src/interview_workbook/leetcode/heap/kth_largest_element.py @@ -1,20 +1,23 @@ """ Kth Largest Element -TODO: Add problem description +Problem: Kth Largest Element in an Array +LeetCode link: https://leetcode.com/problems/kth-largest-element-in-an-array/ +Description: Find the kth largest element in an unsorted array. """ import heapq import random from src.interview_workbook.leetcode._registry import register_problem +from src.interview_workbook.leetcode._runner import TestCase from src.interview_workbook.leetcode._types import Category, Difficulty class Solution: """Find the kth largest element in an unsorted list using a heap.""" - def solve(self, nums: list[int], k: int) -> int: + def findKthLargest(self, nums: list[int], k: int) -> int: """Return the kth largest element.""" if not nums or k < 1 or k > len(nums): return None @@ -26,16 +29,39 @@ def solve(self, nums: list[int], k: int) -> int: return heap[0] +# Example test cases +test_cases = [ + TestCase(([3, 2, 1, 5, 6, 4], 2), 5, "Standard case k=2"), + TestCase(([3, 2, 3, 1, 2, 4, 5, 5, 6], 4), 4, "Duplicates present"), + TestCase(([1], 1), 1, "Single element"), + TestCase(([7, 6, 5, 4, 3, 2, 1], 5), 3, "Sorted descending"), +] + + def demo() -> str: - """Demo for Kth Largest Element.""" + """Run test cases for Kth Largest Element.""" random.seed(0) - nums = [3, 2, 1, 5, 6, 4] - k = 2 - print(f"Initial nums: {nums}, k={k}") - s = Solution() - result = s.solve(nums, k) - print(f"Final result: {result}") - return f"{k}th largest in {nums} is {result}" + sol = Solution() + outputs = [] + outputs.append("Kth Largest Element | LeetCode 215") + outputs.append("=" * 50) + outputs.append("Time: O(n log k) | Space: O(k)") + outputs.append("Technique: Min-heap of size k\n") + + for case in test_cases: + nums, k = case.input_args + res = sol.findKthLargest(list(nums), k) + passed = res == case.expected + status = "✓ PASS" if passed else "✗ FAIL" + outputs.append(f"Test Case: {case.description}") + outputs.append(f" Input: nums={list(nums)}, k={k}") + outputs.append(f" Output: {res}") + outputs.append(f" Expected: {case.expected}") + outputs.append(f" {status}\n") + + result = "\n".join(outputs) + print(result) + return result register_problem( diff --git a/src/interview_workbook/leetcode/heap/last_stone_weight.py b/src/interview_workbook/leetcode/heap/last_stone_weight.py index 1c23cdf..7f4c449 100644 --- a/src/interview_workbook/leetcode/heap/last_stone_weight.py +++ b/src/interview_workbook/leetcode/heap/last_stone_weight.py @@ -10,13 +10,14 @@ import random from src.interview_workbook.leetcode._registry import register_problem +from src.interview_workbook.leetcode._runner import TestCase from src.interview_workbook.leetcode._types import Category, Difficulty class Solution: """Simulate smashing stones using a max-heap.""" - def solve(self, stones: list[int]) -> int: + def lastStoneWeight(self, stones: list[int]) -> int: """Return the weight of the last remaining stone, or 0.""" if not stones: return 0 @@ -33,17 +34,39 @@ def solve(self, stones: list[int]) -> int: return -heap[0] if heap else 0 +# Example test cases +test_cases = [ + TestCase(([2, 7, 4, 1, 8, 1],), 1, "Standard case"), + TestCase(([1],), 1, "Single stone"), + TestCase(([2, 2],), 0, "Two equal stones"), + TestCase(([1, 3],), 2, "Two different stones"), +] + + def demo() -> str: - """Demo for Last Stone Weight.""" + """Run test cases for Last Stone Weight.""" random.seed(0) - stones = [2, 7, 4, 1, 8, 1] - print(f"Initial stones: {stones}") - s = Solution() - result = s.solve(stones) - print(f"Final result: {result}") - output = f"Last stone weight from {stones} is {result}" - print(output) - return output + sol = Solution() + outputs = [] + outputs.append("Last Stone Weight | LeetCode 1046") + outputs.append("=" * 50) + outputs.append("Time: O(n log n) | Space: O(n)") + outputs.append("Technique: Max-heap simulation\n") + + for case in test_cases: + stones = list(case.input_args[0]) + res = sol.lastStoneWeight(stones) + passed = res == case.expected + status = "✓ PASS" if passed else "✗ FAIL" + outputs.append(f"Test Case: {case.description}") + outputs.append(f" Input: stones={list(case.input_args[0])}") + outputs.append(f" Output: {res}") + outputs.append(f" Expected: {case.expected}") + outputs.append(f" {status}\n") + + result = "\n".join(outputs) + print(result) + return result register_problem( diff --git a/src/interview_workbook/leetcode/heap/task_scheduler.py b/src/interview_workbook/leetcode/heap/task_scheduler.py index ac8851f..8f6a60f 100644 --- a/src/interview_workbook/leetcode/heap/task_scheduler.py +++ b/src/interview_workbook/leetcode/heap/task_scheduler.py @@ -11,15 +11,12 @@ from collections import Counter from src.interview_workbook.leetcode._registry import register_problem +from src.interview_workbook.leetcode._runner import TestCase from src.interview_workbook.leetcode._types import Category, Difficulty class Solution: - def solve(self, tasks: list[str], n: int) -> int: - """Alias for least_interval to match demo() convention.""" - return self.least_interval(tasks, n) - - def least_interval(self, tasks: list[str], n: int) -> int: + def leastInterval(self, tasks: list[str], n: int) -> int: """ Return the least number of intervals the CPU will take to finish all tasks. @@ -50,16 +47,42 @@ def least_interval(self, tasks: list[str], n: int) -> int: return time +# Example test cases +test_cases = [ + TestCase((["A", "A", "A", "B", "B", "B"], 2), 8, "Standard case with cooldown"), + TestCase((["A", "A", "A", "B", "B", "B"], 0), 6, "No cooldown"), + TestCase( + (["A", "A", "A", "A", "A", "A", "B", "C", "D", "E", "F", "G"], 2), + 16, + "One dominant task", + ), +] + + def demo() -> str: - """Demonstration of Task Scheduler algorithm with deterministic seeding.""" + """Run test cases for Task Scheduler.""" random.seed(0) - tasks = ["A", "A", "A", "B", "B", "B"] - n = 2 - print(f"Initial tasks: {tasks}, cooldown={n}") - s = Solution() - result = s.solve(tasks, n) - print(f"Final result: {result}") - return f"Least intervals to finish tasks {tasks} with cooldown {n} -> {result}" + sol = Solution() + outputs = [] + outputs.append("Task Scheduler | LeetCode 621") + outputs.append("=" * 50) + outputs.append("Time: O(n log 26) | Space: O(26)") + outputs.append("Technique: Max-heap with cooling cycle\n") + + for case in test_cases: + tasks, n = case.input_args + res = sol.leastInterval(list(tasks), n) + passed = res == case.expected + status = "✓ PASS" if passed else "✗ FAIL" + outputs.append(f"Test Case: {case.description}") + outputs.append(f" Input: tasks={list(tasks)}, n={n}") + outputs.append(f" Output: {res}") + outputs.append(f" Expected: {case.expected}") + outputs.append(f" {status}\n") + + result = "\n".join(outputs) + print(result) + return result register_problem( diff --git a/src/interview_workbook/leetcode/intervals/insert_interval.py b/src/interview_workbook/leetcode/intervals/insert_interval.py index 42705ea..bb4aace 100644 --- a/src/interview_workbook/leetcode/intervals/insert_interval.py +++ b/src/interview_workbook/leetcode/intervals/insert_interval.py @@ -1,23 +1,21 @@ """ Insert Interval -TODO: Add problem description +Problem: Insert Interval +LeetCode link: https://leetcode.com/problems/insert-interval/ +Description: Insert a new interval into a list of non-overlapping intervals, merging if necessary. """ from src.interview_workbook.leetcode._registry import register_problem +from src.interview_workbook.leetcode._runner import TestCase from src.interview_workbook.leetcode._types import Category, Difficulty class Solution: - def solve(self, *args): + def insert(self, intervals: list[list[int]], newInterval: list[int]) -> list[list[int]]: """ Insert Interval: Given intervals and a new interval, insert and merge. - Args: - intervals (List[List[int]]), newInterval (List[int]) - Returns: - List[List[int]] """ - intervals, newInterval = args res = [] i = 0 n = len(intervals) @@ -38,15 +36,49 @@ def solve(self, *args): return res -def demo(): - """Run a simple demonstration for Insert Interval problem.""" - s = Solution() - intervals = [[1, 3], [6, 9]] - newInterval = [2, 5] - print(f"Initial intervals: {intervals}, newInterval: {newInterval}") - result = s.solve(intervals, newInterval) - print(f"Output: {result}") - return str(result) +# Example test cases +test_cases = [ + TestCase( + ([[1, 3], [6, 9]], [2, 5]), + [[1, 5], [6, 9]], + "Merge with first interval", + ), + TestCase( + ([[1, 2], [3, 5], [6, 7], [8, 10], [12, 16]], [4, 8]), + [[1, 2], [3, 10], [12, 16]], + "Merge multiple intervals", + ), + TestCase(([], [5, 7]), [[5, 7]], "Empty intervals list"), + TestCase(([[1, 5]], [2, 3]), [[1, 5]], "New interval inside existing"), +] + + +def demo() -> str: + """Run test cases for Insert Interval.""" + sol = Solution() + outputs = [] + outputs.append("Insert Interval | LeetCode 57") + outputs.append("=" * 50) + outputs.append("Time: O(n) | Space: O(n)") + outputs.append("Technique: Linear scan with merge\n") + + for case in test_cases: + intervals, new_int = case.input_args + # Copy to avoid mutation + intervals_copy = [list(x) for x in intervals] + new_int_copy = list(new_int) + res = sol.insert(intervals_copy, new_int_copy) + passed = res == case.expected + status = "✓ PASS" if passed else "✗ FAIL" + outputs.append(f"Test Case: {case.description}") + outputs.append(f" Input: intervals={intervals}, newInterval={new_int}") + outputs.append(f" Output: {res}") + outputs.append(f" Expected: {case.expected}") + outputs.append(f" {status}\n") + + result = "\n".join(outputs) + print(result) + return result register_problem( diff --git a/src/interview_workbook/leetcode/intervals/meeting_rooms.py b/src/interview_workbook/leetcode/intervals/meeting_rooms.py index e98e3e9..f97318a 100644 --- a/src/interview_workbook/leetcode/intervals/meeting_rooms.py +++ b/src/interview_workbook/leetcode/intervals/meeting_rooms.py @@ -1,23 +1,21 @@ """ Meeting Rooms -TODO: Add problem description +Problem: Meeting Rooms +LeetCode link: https://leetcode.com/problems/meeting-rooms/ +Description: Given an array of meeting time intervals, determine if a person can attend all meetings. """ from src.interview_workbook.leetcode._registry import register_problem +from src.interview_workbook.leetcode._runner import TestCase from src.interview_workbook.leetcode._types import Category, Difficulty class Solution: - def solve(self, *args): + def canAttendMeetings(self, intervals: list[list[int]]) -> bool: """ Meeting Rooms: Check if a person can attend all meetings. - Args: - intervals (List[List[int]]) - Returns: - bool """ - (intervals,) = args intervals.sort(key=lambda x: x[0]) for i in range(1, len(intervals)): if intervals[i][0] < intervals[i - 1][1]: @@ -25,16 +23,38 @@ def solve(self, *args): return True -def demo(): - """Run a simple demonstration for Meeting Rooms problem.""" - s = Solution() - intervals1 = [[0, 30], [5, 10], [15, 20]] - intervals2 = [[7, 10], [2, 4]] - result1 = s.solve(intervals1) - result2 = s.solve(intervals2) - print(f"Intervals: {intervals1}, Can Attend All: {result1}") - print(f"Intervals: {intervals2}, Can Attend All: {result2}") - return f"{intervals1} -> {result1}; {intervals2} -> {result2}" +# Example test cases +test_cases = [ + TestCase(([[0, 30], [5, 10], [15, 20]],), False, "Overlapping meetings"), + TestCase(([[7, 10], [2, 4]],), True, "Non-overlapping meetings"), + TestCase(([[1, 5], [5, 10]],), True, "Adjacent meetings (no overlap)"), + TestCase(([],), True, "Empty intervals"), +] + + +def demo() -> str: + """Run test cases for Meeting Rooms.""" + sol = Solution() + outputs = [] + outputs.append("Meeting Rooms | LeetCode 252") + outputs.append("=" * 50) + outputs.append("Time: O(n log n) | Space: O(1)") + outputs.append("Technique: Sort by start time, check overlaps\n") + + for case in test_cases: + intervals = [list(x) for x in case.input_args[0]] # Copy + res = sol.canAttendMeetings(intervals) + passed = res == case.expected + status = "✓ PASS" if passed else "✗ FAIL" + outputs.append(f"Test Case: {case.description}") + outputs.append(f" Input: intervals={case.input_args[0]}") + outputs.append(f" Output: {res}") + outputs.append(f" Expected: {case.expected}") + outputs.append(f" {status}\n") + + result = "\n".join(outputs) + print(result) + return result register_problem( diff --git a/src/interview_workbook/leetcode/intervals/meeting_rooms_ii.py b/src/interview_workbook/leetcode/intervals/meeting_rooms_ii.py index 1243ab2..b73cba0 100644 --- a/src/interview_workbook/leetcode/intervals/meeting_rooms_ii.py +++ b/src/interview_workbook/leetcode/intervals/meeting_rooms_ii.py @@ -1,23 +1,21 @@ """ -Meeting Rooms Ii +Meeting Rooms II -TODO: Add problem description +Problem: Meeting Rooms II +LeetCode link: https://leetcode.com/problems/meeting-rooms-ii/ +Description: Given an array of meeting time intervals, find the minimum number of conference rooms required. """ from src.interview_workbook.leetcode._registry import register_problem +from src.interview_workbook.leetcode._runner import TestCase from src.interview_workbook.leetcode._types import Category, Difficulty class Solution: - def solve(self, *args): + def minMeetingRooms(self, intervals: list[list[int]]) -> int: """ Meeting Rooms II: Minimum meeting rooms required. - Args: - intervals (List[List[int]]) - Returns: - int """ - (intervals,) = args if not intervals: return 0 starts = sorted([i[0] for i in intervals]) @@ -37,16 +35,42 @@ def solve(self, *args): return rooms -def demo(): - """Run a simple demonstration for Meeting Rooms II problem.""" - s = Solution() - intervals1 = [[0, 30], [5, 10], [15, 20]] - intervals2 = [[7, 10], [2, 4]] - result1 = s.solve(intervals1) - result2 = s.solve(intervals2) - print(f"Intervals: {intervals1}, Meeting Rooms Needed: {result1}") - print(f"Intervals: {intervals2}, Meeting Rooms Needed: {result2}") - return f"{intervals1} -> {result1}; {intervals2} -> {result2}" +# Example test cases +test_cases = [ + TestCase(([[0, 30], [5, 10], [15, 20]],), 2, "Overlapping meetings need 2 rooms"), + TestCase(([[7, 10], [2, 4]],), 1, "Non-overlapping meetings need 1 room"), + TestCase(([[1, 5], [5, 10], [10, 15]],), 1, "Sequential meetings"), + TestCase( + ([[1, 10], [2, 7], [3, 19], [8, 12], [10, 20], [11, 30]],), + 4, + "Complex overlapping", + ), +] + + +def demo() -> str: + """Run test cases for Meeting Rooms II.""" + sol = Solution() + outputs = [] + outputs.append("Meeting Rooms II | LeetCode 253") + outputs.append("=" * 50) + outputs.append("Time: O(n log n) | Space: O(n)") + outputs.append("Technique: Two-pointer on sorted start/end times\n") + + for case in test_cases: + intervals = [list(x) for x in case.input_args[0]] # Copy + res = sol.minMeetingRooms(intervals) + passed = res == case.expected + status = "✓ PASS" if passed else "✗ FAIL" + outputs.append(f"Test Case: {case.description}") + outputs.append(f" Input: intervals={case.input_args[0]}") + outputs.append(f" Output: {res}") + outputs.append(f" Expected: {case.expected}") + outputs.append(f" {status}\n") + + result = "\n".join(outputs) + print(result) + return result register_problem( diff --git a/src/interview_workbook/leetcode/intervals/merge_intervals.py b/src/interview_workbook/leetcode/intervals/merge_intervals.py index aa7f955..b715a4c 100644 --- a/src/interview_workbook/leetcode/intervals/merge_intervals.py +++ b/src/interview_workbook/leetcode/intervals/merge_intervals.py @@ -7,19 +7,19 @@ """ from src.interview_workbook.leetcode._registry import register_problem +from src.interview_workbook.leetcode._runner import TestCase from src.interview_workbook.leetcode._types import Category, Difficulty class Solution: - def solve(self, *args): + def merge(self, intervals: list[list[int]]) -> list[list[int]]: """ - Merge Intervals: Merge overlapping intervals. + Merge overlapping intervals. Args: - intervals (List[List[int]]) + intervals: List of [start, end] intervals Returns: - List[List[int]] + List of merged non-overlapping intervals """ - (intervals,) = args if not intervals: return [] intervals.sort(key=lambda x: x[0]) @@ -32,13 +32,43 @@ def solve(self, *args): return merged -def demo(): - """Run a simple demonstration for Merge Intervals problem.""" - s = Solution() - intervals = [[1, 3], [2, 6], [8, 10], [15, 18]] - result = s.solve(intervals) - print(f"Initial intervals: {intervals}, Merged intervals: {result}") - return f"{intervals} -> {result}" +# Example test cases +test_cases = [ + TestCase( + ([[1, 3], [2, 6], [8, 10], [15, 18]],), + [[1, 6], [8, 10], [15, 18]], + "Overlapping and non-overlapping intervals", + ), + TestCase(([[1, 4], [4, 5]],), [[1, 5]], "Adjacent intervals"), + TestCase(([[1, 4], [0, 4]],), [[0, 4]], "Second interval starts before first"), + TestCase(([[1, 4]],), [[1, 4]], "Single interval"), +] + + +def demo() -> str: + """Run test cases for Merge Intervals.""" + sol = Solution() + outputs = [] + outputs.append("Merge Intervals | LeetCode 56") + outputs.append("=" * 50) + outputs.append("Time: O(n log n) | Space: O(n)") + outputs.append("Technique: Sort by start, merge overlapping\n") + + for case in test_cases: + # Copy to avoid mutation + intervals_copy = [list(x) for x in case.input_args[0]] + res = sol.merge(intervals_copy) + passed = res == case.expected + status = "✓ PASS" if passed else "✗ FAIL" + outputs.append(f"Test Case: {case.description}") + outputs.append(f" Input: intervals={case.input_args[0]}") + outputs.append(f" Output: {res}") + outputs.append(f" Expected: {case.expected}") + outputs.append(f" {status}\n") + + result = "\n".join(outputs) + print(result) + return result register_problem( diff --git a/src/interview_workbook/leetcode/intervals/non_overlapping_intervals.py b/src/interview_workbook/leetcode/intervals/non_overlapping_intervals.py index ccc4191..bc8ea22 100644 --- a/src/interview_workbook/leetcode/intervals/non_overlapping_intervals.py +++ b/src/interview_workbook/leetcode/intervals/non_overlapping_intervals.py @@ -1,23 +1,21 @@ """ Non Overlapping Intervals -TODO: Add problem description +Problem: Non-overlapping Intervals +LeetCode link: https://leetcode.com/problems/non-overlapping-intervals/ +Description: Given an array of intervals, return the minimum number of intervals to remove to make the rest non-overlapping. """ from src.interview_workbook.leetcode._registry import register_problem +from src.interview_workbook.leetcode._runner import TestCase from src.interview_workbook.leetcode._types import Category, Difficulty class Solution: - def solve(self, *args): + def eraseOverlapIntervals(self, intervals: list[list[int]]) -> int: """ Non-overlapping Intervals: Minimum to remove to avoid overlap. - Args: - intervals (List[List[int]]) - Returns: - int """ - (intervals,) = args if not intervals: return 0 intervals.sort(key=lambda x: x[1]) @@ -31,13 +29,38 @@ def solve(self, *args): return count -def demo(): - """Run a simple demonstration for Non-overlapping Intervals problem.""" - s = Solution() - intervals = [[1, 2], [2, 3], [3, 4], [1, 3]] - result = s.solve(intervals) - print(f"Initial intervals: {intervals}, Minimum to remove: {result}") - return f"{intervals} -> {result}" +# Example test cases +test_cases = [ + TestCase(([[1, 2], [2, 3], [3, 4], [1, 3]],), 1, "Remove [1,3] to avoid overlap"), + TestCase(([[1, 2], [1, 2], [1, 2]],), 2, "Remove 2 duplicate intervals"), + TestCase(([[1, 2], [2, 3]],), 0, "Already non-overlapping"), + TestCase(([[1, 100], [11, 22], [1, 11], [2, 12]],), 2, "One long interval overlaps many"), +] + + +def demo() -> str: + """Run test cases for Non-overlapping Intervals.""" + sol = Solution() + outputs = [] + outputs.append("Non-overlapping Intervals | LeetCode 435") + outputs.append("=" * 50) + outputs.append("Time: O(n log n) | Space: O(1)") + outputs.append("Technique: Greedy - sort by end time, count overlaps\n") + + for case in test_cases: + intervals = [list(x) for x in case.input_args[0]] # Copy + res = sol.eraseOverlapIntervals(intervals) + passed = res == case.expected + status = "✓ PASS" if passed else "✗ FAIL" + outputs.append(f"Test Case: {case.description}") + outputs.append(f" Input: intervals={case.input_args[0]}") + outputs.append(f" Output: {res}") + outputs.append(f" Expected: {case.expected}") + outputs.append(f" {status}\n") + + result = "\n".join(outputs) + print(result) + return result register_problem(