diff --git a/Datastructures + Algorithms/0_Problem_Solving_Patterns.ipynb b/Datastructures + Algorithms/0_Problem_Solving_Patterns.ipynb index 126da8a..3e4ad14 100644 --- a/Datastructures + Algorithms/0_Problem_Solving_Patterns.ipynb +++ b/Datastructures + Algorithms/0_Problem_Solving_Patterns.ipynb @@ -10,202 +10,970 @@ }, { "cell_type": "markdown", - "id": "bec0fd2f", + "id": "e392e626", "metadata": {}, "source": [ - "## Sliding Window\n" + "## Two pointers\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bd466991", + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "### Two Pointers\n", + "\n", + "- Use two pointers to solve problems efficiently\n", + "- Pointers can move towards each other, one faster than the other, or in same direction\n", + "- Pointers can be on different arrays or same array\n", + "- Useful for finding pairs, reversing, partitioning\n", + "- Time: O(n), Space: O(1) typically\n", + "\"\"\"\n", + "\n", + "# Example: Reverse a string\n", + "def reverse_string(s: str) -> str:\n", + " chars = list(s)\n", + " left, right = 0, len(chars) - 1\n", + " \n", + " while left < right:\n", + " chars[left], chars[right] = chars[right], chars[left]\n", + " left += 1\n", + " right -= 1\n", + " \n", + " return \"\".join(chars)\n", + "\n", + "print(reverse_string(\"hello\")) # \"olleh\"\n", + "\n", + "# Example: Two Sum (find two numbers that add to target)\n", + "def two_sum(nums: list[int], target: int) -> list[int]:\n", + " nums.sort()\n", + " left, right = 0, len(nums) - 1\n", + " \n", + " while left < right:\n", + " current_sum = nums[left] + nums[right]\n", + " if current_sum == target:\n", + " return [left, right]\n", + " elif current_sum < target:\n", + " left += 1\n", + " else:\n", + " right -= 1\n", + " \n", + " return []\n", + "\n", + "print(two_sum([2, 7, 11, 15], 9)) # [0, 1]" ] }, { "cell_type": "markdown", - "id": "8a821bd0", + "id": "d1b6e914", "metadata": {}, "source": [ - "- two pointers usually move in the same direction will never overtake each other.\n", - "- This ensures that each value is only visited at most twice and the time complexity is still O(n).\n" + "## Fast & Slow Pointers\n" ] }, { "cell_type": "code", "execution_count": null, - "id": "ff281de0", + "id": "167d42e8", "metadata": {}, "outputs": [], - "source": [] + "source": [ + "## Fast & Slow Pointers\n", + "\n", + "\"\"\"\n", + "### Fast & Slow Pointers\n", + "\n", + "- Move two pointers through array/linked list at different speeds\n", + "- Fast pointer moves 2 steps, slow moves 1 step\n", + "- Used to detect cycles, find middle, or remove duplicates\n", + "- By moving at different speeds, pointers are bound to meet in a cycle\n", + "- Time: O(n), Space: O(1)\n", + "\"\"\"\n", + "\n", + "# Example: Detect cycle in linked list\n", + "class ListNode:\n", + " def __init__(self, val=0, next=None):\n", + " self.val = val\n", + " self.next = next\n", + "\n", + "def has_cycle(head: ListNode) -> bool:\n", + " if not head or not head.next:\n", + " return False\n", + " \n", + " slow = head\n", + " fast = head.next\n", + " \n", + " while slow != fast:\n", + " if not fast or not fast.next:\n", + " return False\n", + " slow = slow.next\n", + " fast = fast.next.next\n", + " \n", + " return True # Cycle detected\n", + "\n", + "# Example: Find middle of array\n", + "def find_middle(arr: list[int]) -> int:\n", + " slow = fast = 0\n", + " \n", + " while fast < len(arr) - 1 and fast + 1 < len(arr):\n", + " slow += 1\n", + " fast += 2\n", + " \n", + " return arr[slow]\n", + "\n", + "print(find_middle([1, 2, 3, 4, 5])) # 3\n", + "print(find_middle([1, 2, 3, 4])) # 2 or 3" + ] }, { "cell_type": "markdown", - "id": "e392e626", + "id": "bec0fd2f", "metadata": {}, "source": [ - "## Two pointers\n" + "## Sliding Window\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ff281de0", + "metadata": {}, + "outputs": [], + "source": [ + "## Sliding Window\n", + "\n", + "\"\"\"\n", + "### Sliding Window\n", + "\n", + "- Maintain a window of elements that slides across the array\n", + "- Two pointers (left and right) define the window boundaries\n", + "- Expand window by moving right pointer, shrink by moving left\n", + "- Both pointers move in same direction, never overtake each other\n", + "- Each element visited at most twice: O(n)\n", + "- Used for: max/min in subarray, longest substring problems\n", + "\"\"\"\n", + "\n", + "# Example: Maximum sum of subarray of size k\n", + "def max_sum_subarray(arr: list[int], k: int) -> int:\n", + " if k > len(arr):\n", + " return -1\n", + " \n", + " # Calculate sum of first window\n", + " window_sum = sum(arr[:k])\n", + " max_sum = window_sum\n", + " \n", + " # Slide the window\n", + " for i in range(k, len(arr)):\n", + " window_sum = window_sum - arr[i - k] + arr[i]\n", + " max_sum = max(max_sum, window_sum)\n", + " \n", + " return max_sum\n", + "\n", + "print(max_sum_subarray([1, 4, 2, 10, 2, 3, 1, 0, 20], 4)) # 24 (10+2+3+1)\n", + "\n", + "# Example: Longest substring without repeating characters\n", + "def longest_substring(s: str) -> int:\n", + " char_index = {}\n", + " max_length = 0\n", + " left = 0\n", + " \n", + " for right in range(len(s)):\n", + " if s[right] in char_index and char_index[s[right]] >= left:\n", + " left = char_index[s[right]] + 1\n", + " \n", + " char_index[s[right]] = right\n", + " max_length = max(max_length, right - left + 1)\n", + " \n", + " return max_length\n", + "\n", + "print(longest_substring(\"abcabcbb\")) # 3 (\"abc\")\n", + "print(longest_substring(\"pwwkew\")) # 3 (\"wke\")" ] }, { "cell_type": "markdown", - "id": "cb0dbcb4", + "id": "79b0512d", "metadata": {}, "source": [ - "- pointers can cross each other and can be on different arrays.\n" + "## Merge Intervals\n" ] }, { "cell_type": "code", "execution_count": null, - "id": "bd466991", + "id": "cbf3a52e", "metadata": {}, "outputs": [], - "source": [] + "source": [ + "## Merge Intervals\n", + "\n", + "\"\"\"\n", + "### Merge Intervals\n", + "\n", + "- Sort intervals by start point\n", + "- Iterate through sorted intervals and merge overlapping ones\n", + "- Check if current interval overlaps with last merged interval\n", + "- If overlaps: extend end of last interval\n", + "- If no overlap: add current interval to result\n", + "- Time: O(n log n) for sorting, Space: O(1) or O(n) for result\n", + "\"\"\"\n", + "\n", + "# Example: Merge overlapping intervals\n", + "def merge_intervals(intervals: list[list[int]]) -> list[list[int]]:\n", + " if not intervals:\n", + " return []\n", + " \n", + " # Sort by start time\n", + " intervals.sort(key=lambda x: x[0])\n", + " merged = [intervals[0]]\n", + " \n", + " for current in intervals[1:]:\n", + " last = merged[-1]\n", + " \n", + " # If current overlaps with last, merge them\n", + " if current[0] <= last[1]:\n", + " merged[-1] = [last[0], max(last[1], current[1])]\n", + " else:\n", + " merged.append(current)\n", + " \n", + " return merged\n", + "\n", + "print(merge_intervals([[1,3],[2,6],[8,10],[15,18]]))\n", + "# Output: [[1,6],[8,10],[15,18]]\n", + "\n", + "# Example: Insert interval\n", + "def insert_interval(intervals: list[list[int]], new: list[int]) -> list[list[int]]:\n", + " result = []\n", + " i = 0\n", + " \n", + " # Add all intervals that end before new interval starts\n", + " while i < len(intervals) and intervals[i][1] < new[0]:\n", + " result.append(intervals[i])\n", + " i += 1\n", + " \n", + " # Merge overlapping intervals\n", + " while i < len(intervals) and intervals[i][0] <= new[1]:\n", + " new[0] = min(new[0], intervals[i][0])\n", + " new[1] = max(new[1], intervals[i][1])\n", + " i += 1\n", + " \n", + " result.append(new)\n", + " \n", + " # Add remaining intervals\n", + " while i < len(intervals):\n", + " result.append(intervals[i])\n", + " i += 1\n", + " \n", + " return result\n", + "\n", + "print(insert_interval([[1,5]], [2,7])) # [[1,7]]" + ] }, { "cell_type": "markdown", - "id": "d1b6e914", + "id": "c9f6f188", "metadata": {}, "source": [ - "## Fast & Slow Pointers\n" + "## Cyclic Sort\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3eebcb0a", + "metadata": {}, + "outputs": [], + "source": [ + "## Cyclic Sort\n", + "\n", + "\"\"\"\n", + "### Cyclic Sort\n", + "\n", + "- Sort array with numbers in range 1 to n\n", + "- Place each number at its correct index (number x at index x-1)\n", + "- Iterate through array and place numbers in correct positions\n", + "- If number is already in correct position or out of range, move to next\n", + "- Time: O(n), Space: O(1)\n", + "- Used for: finding missing numbers, duplicates, or unordered elements\n", + "\"\"\"\n", + "\n", + "# Example: Sort array with numbers 1 to n\n", + "def cyclic_sort(nums: list[int]) -> list[int]:\n", + " i = 0\n", + " while i < len(nums):\n", + " # Number should be at index number-1\n", + " correct_index = nums[i] - 1\n", + " \n", + " if nums[i] != nums[correct_index]:\n", + " # Swap\n", + " nums[i], nums[correct_index] = nums[correct_index], nums[i]\n", + " else:\n", + " i += 1\n", + " \n", + " return nums\n", + "\n", + "print(cyclic_sort([3, 1, 4, 2])) # [1, 2, 3, 4]\n", + "\n", + "# Example: Find missing number\n", + "def find_missing_number(nums: list[int]) -> int:\n", + " # Sort using cyclic sort\n", + " i = 0\n", + " while i < len(nums):\n", + " correct_index = nums[i]\n", + " if nums[i] < len(nums) and nums[i] != nums[correct_index]:\n", + " nums[i], nums[correct_index] = nums[correct_index], nums[i]\n", + " else:\n", + " i += 1\n", + " \n", + " # Find missing\n", + " for i in range(len(nums)):\n", + " if nums[i] != i:\n", + " return i\n", + " \n", + " return len(nums)\n", + "\n", + "print(find_missing_number([0, 1, 3])) # 2" ] }, { "cell_type": "markdown", - "id": "0fc22909", + "id": "a9f60046", "metadata": {}, "source": [ - "- uses two pointers which move through the array (or sequence/LinkedList) at different speeds\n", - "- By moving at different speeds (say, in a cyclic LinkedList), the algorithm proves that the two pointers are bound to meet.\n", - "- The fast pointer should catch the slow pointer once both the pointers are in a cyclic loop.\n" + "## In-place Reversal of a Linked List\n" ] }, { "cell_type": "code", "execution_count": null, - "id": "167d42e8", + "id": "429b5f64", "metadata": {}, "outputs": [], - "source": [] + "source": [ + "## In-place Reversal of a Linked List\n", + "\n", + "\"\"\"\n", + "### In-place Reversal of a Linked List\n", + "\n", + "- Reverse linked list without creating new list\n", + "- Keep track of previous, current, and next nodes\n", + "- Reverse the link from current to previous\n", + "- Move pointers forward\n", + "- Time: O(n), Space: O(1)\n", + "\"\"\"\n", + "\n", + "class ListNode:\n", + " def __init__(self, val=0, next=None):\n", + " self.val = val\n", + " self.next = next\n", + "\n", + "def reverse_linked_list(head: ListNode) -> ListNode:\n", + " prev = None\n", + " current = head\n", + " \n", + " while current:\n", + " # Store next node\n", + " next_node = current.next\n", + " \n", + " # Reverse the link\n", + " current.next = prev\n", + " \n", + " # Move pointers forward\n", + " prev = current\n", + " current = next_node\n", + " \n", + " return prev # New head\n", + "\n", + "# Helper to create linked list from array\n", + "def create_list(arr: list[int]) -> ListNode:\n", + " if not arr:\n", + " return None\n", + " head = ListNode(arr[0])\n", + " current = head\n", + " for val in arr[1:]:\n", + " current.next = ListNode(val)\n", + " current = current.next\n", + " return head\n", + "\n", + "# Helper to print linked list\n", + "def print_list(head: ListNode):\n", + " result = []\n", + " while head:\n", + " result.append(head.val)\n", + " head = head.next\n", + " print(result)\n", + "\n", + "head = create_list([1, 2, 3, 4, 5])\n", + "reversed_head = reverse_linked_list(head)\n", + "print_list(reversed_head) # [5, 4, 3, 2, 1]" + ] }, { "cell_type": "markdown", - "id": "5e10b9b6", + "id": "7d0af651", "metadata": {}, "source": [ - "## Traversing from the right\n" + "## Stacks\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9eda98e2", + "metadata": {}, + "outputs": [], + "source": [ + "## Stacks\n", + "\n", + "\"\"\"\n", + "### Stacks (LIFO - Last In First Out)\n", + "\n", + "- Last element added is first element removed\n", + "- Use for: parsing, backtracking, undo/redo, expression evaluation\n", + "- Push (add), Pop (remove), Peek (view top)\n", + "- Time: O(1) for push/pop, Space: O(n)\n", + "\"\"\"\n", + "\n", + "# Example: Valid parentheses\n", + "def is_valid_parentheses(s: str) -> bool:\n", + " stack = []\n", + " pairs = {'(': ')', '[': ']', '{': '}'}\n", + " \n", + " for char in s:\n", + " if char in pairs:\n", + " stack.append(char)\n", + " elif char in pairs.values():\n", + " if not stack or pairs[stack.pop()] != char:\n", + " return False\n", + " \n", + " return len(stack) == 0\n", + "\n", + "print(is_valid_parentheses(\"()[]{}\")) # True\n", + "print(is_valid_parentheses(\"([)]\")) # False\n", + "print(is_valid_parentheses(\"{[]}\")) # True\n", + "\n", + "# Example: Evaluate reverse Polish notation\n", + "def eval_rpn(tokens: list[str]) -> int:\n", + " stack = []\n", + " operators = {'+', '-', '*', '/'}\n", + " \n", + " for token in tokens:\n", + " if token in operators:\n", + " b = stack.pop()\n", + " a = stack.pop()\n", + " if token == '+':\n", + " stack.append(a + b)\n", + " elif token == '-':\n", + " stack.append(a - b)\n", + " elif token == '*':\n", + " stack.append(a * b)\n", + " elif token == '/':\n", + " stack.append(int(a / b))\n", + " else:\n", + " stack.append(int(token))\n", + " \n", + " return stack[0]\n", + "\n", + "print(eval_rpn([\"2\", \"1\", \"+\", \"3\", \"*\"])) # 9 ((2+1)*3)\n", + "print(eval_rpn([\"4\", \"13\", \"5\", \"/\", \"+\"])) # 6 (4+(13/5))" ] }, { "cell_type": "markdown", - "id": "30d02bdd", + "id": "29793ab1", "metadata": {}, "source": [ - "- Sometimes you can traverse the array starting from the right instead of the conventional approach of from the left.\n" + "## Monotonic Stack\n" ] }, { "cell_type": "code", "execution_count": null, - "id": "ec4202d2", + "id": "008790e4", "metadata": {}, "outputs": [], - "source": [] + "source": [ + "## Monotonic Stack\n", + "\n", + "\"\"\"\n", + "### Monotonic Stack\n", + "\n", + "- Stack that maintains elements in increasing or decreasing order\n", + "- When adding new element, pop elements that violate order\n", + "- Used for: next greater element, trapping water, stock span\n", + "- Time: O(n), Space: O(n)\n", + "\"\"\"\n", + "\n", + "# Example: Next Greater Element\n", + "def next_greater_element(arr: list[int]) -> list[int]:\n", + " stack = []\n", + " result = [-1] * len(arr)\n", + " \n", + " # Process from right to left\n", + " for i in range(len(arr) - 1, -1, -1):\n", + " # Pop smaller elements\n", + " while stack and stack[-1] <= arr[i]:\n", + " stack.pop()\n", + " \n", + " # Top of stack is next greater (if exists)\n", + " if stack:\n", + " result[i] = stack[-1]\n", + " \n", + " stack.append(arr[i])\n", + " \n", + " return result\n", + "\n", + "print(next_greater_element([1, 5, 0, 3, 4, 5]))\n", + "# [5, -1, 3, 4, 5, -1]\n", + "\n", + "# Example: Trapping Rain Water\n", + "def trap_water(height: list[int]) -> int:\n", + " if not height:\n", + " return 0\n", + " \n", + " stack = []\n", + " water = 0\n", + " \n", + " for i in range(len(height)):\n", + " while stack and height[i] > height[stack[-1]]:\n", + " top = stack.pop()\n", + " \n", + " if not stack:\n", + " break\n", + " \n", + " width = i - stack[-1] - 1\n", + " h = min(height[i], height[stack[-1]]) - height[top]\n", + " water += width * h\n", + " \n", + " stack.append(i)\n", + " \n", + " return water\n", + "\n", + "print(trap_water([0,1,0,2,1,0,1,3,2,1,2,1])) # 6" + ] }, { "cell_type": "markdown", - "id": "8fda7d53", + "id": "d1c72896", "metadata": {}, "source": [ - "## Sorting the array\n" + "## Hash Maps\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0f32e859", + "metadata": {}, + "outputs": [], + "source": [ + "## Hash Maps\n", + "\n", + "\"\"\"\n", + "### Hash Maps (Dictionaries in Python)\n", + "\n", + "- Fast lookup, insertion, deletion: O(1) average\n", + "- Map keys to values\n", + "- Used for: frequency counting, caching, finding pairs, grouping\n", + "- Space: O(n) for storing n elements\n", + "\"\"\"\n", + "\n", + "# Example: Two Sum (find two numbers that add to target)\n", + "def two_sum_hash(nums: list[int], target: int) -> list[int]:\n", + " num_map = {}\n", + " \n", + " for i, num in enumerate(nums):\n", + " complement = target - num\n", + " \n", + " if complement in num_map:\n", + " return [num_map[complement], i]\n", + " \n", + " num_map[num] = i\n", + " \n", + " return []\n", + "\n", + "print(two_sum_hash([2, 7, 11, 15], 9)) # [0, 1]\n", + "\n", + "# Example: Anagram grouping\n", + "from collections import defaultdict\n", + "\n", + "def group_anagrams(strs: list[str]) -> list[list[str]]:\n", + " anagram_map = defaultdict(list)\n", + " \n", + " for word in strs:\n", + " # Sort characters to get canonical form\n", + " sorted_word = \"\".join(sorted(word))\n", + " anagram_map[sorted_word].append(word)\n", + " \n", + " return list(anagram_map.values())\n", + "\n", + "print(group_anagrams([\"eat\", \"tea\", \"ate\", \"eat\", \"tan\", \"ate\", \"nat\"]))\n", + "# [[\"eat\", \"tea\", \"ate\", \"eat\", \"ate\"], [\"tan\", \"nat\"]]" ] }, { "cell_type": "markdown", - "id": "cb773498", + "id": "e2bf6c0b", "metadata": {}, "source": [ - "- Sometimes sorting the array first may significantly simplify the problem\n" + "## Level Order Traversal Pattern\n" ] }, { "cell_type": "code", "execution_count": null, - "id": "cb116bf3", + "id": "c90cb347", "metadata": {}, "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "9b1c5be5", + "metadata": {}, + "source": [ + "## Tree Breadth First Search\n" + ] + }, + { + "cell_type": "markdown", + "id": "b378a4b6", + "metadata": {}, + "source": [ + "## Tree Depth First Search\n" + ] + }, + { + "cell_type": "markdown", + "id": "90975236", + "metadata": {}, + "source": [ + "## Graphs\n" + ] + }, + { + "cell_type": "markdown", + "id": "82906705", + "metadata": {}, + "source": [ + "## Island (Matrix Traversal)\n" + ] + }, + { + "cell_type": "markdown", + "id": "7d726e24", + "metadata": {}, "source": [ - "#####sdfasdfasdg#########afsdfasdfsadfasdfasdfasdfasdfasdfsdfdsa" + "## Two Heaps\n" ] }, { "cell_type": "markdown", - "id": "42c30436", + "id": "0a4b727d", "metadata": {}, "source": [ - "## Precomputation\n" + "## Subsets\n" ] }, { "cell_type": "markdown", - "id": "07bee308", + "id": "5d3d94d8", "metadata": {}, "source": [ - "- For questions where summation or multiplication of a subarray is involved, pre-computation using hashing or a prefix/suffix sum/product might be useful.\n" + "## Modified Binary Search\n" ] }, { "cell_type": "code", "execution_count": null, - "id": "ff247028", + "id": "9229ae9f", "metadata": {}, "outputs": [], - "source": [] + "source": [ + "## Modified Binary Search\n", + "\n", + "\"\"\"\n", + "### Modified Binary Search\n", + "\n", + "- Binary search adapted for: rotated arrays, find first/last occurrence\n", + "- Works on sorted or partially sorted data\n", + "- Time: O(log n), Space: O(1)\n", + "\"\"\"\n", + "\n", + "# Example: Search in rotated sorted array\n", + "def search_rotated_array(nums: list[int], target: int) -> int:\n", + " left, right = 0, len(nums) - 1\n", + " \n", + " while left <= right:\n", + " mid = (left + right) // 2\n", + " \n", + " if nums[mid] == target:\n", + " return mid\n", + " \n", + " # Check which side is sorted\n", + " if nums[left] <= nums[mid]:\n", + " # Left side is sorted\n", + " if nums[left] <= target < nums[mid]:\n", + " right = mid - 1\n", + " else:\n", + " left = mid + 1\n", + " else:\n", + " # Right side is sorted\n", + " if nums[mid] < target <= nums[right]:\n", + " left = mid + 1\n", + " else:\n", + " right = mid - 1\n", + " \n", + " return -1\n", + "\n", + "print(search_rotated_array([4,5,6,7,0,1,2], 0)) # 4\n", + "\n", + "# Example: Find first and last occurrence\n", + "def find_first_last(nums: list[int], target: int) -> list[int]:\n", + " def find_first():\n", + " left, right = 0, len(nums) - 1\n", + " result = -1\n", + " while left <= right:\n", + " mid = (left + right) // 2\n", + " if nums[mid] == target:\n", + " result = mid\n", + " right = mid - 1\n", + " elif nums[mid] < target:\n", + " left = mid + 1\n", + " else:\n", + " right = mid - 1\n", + " return result\n", + " \n", + " def find_last():\n", + " left, right = 0, len(nums) - 1\n", + " result = -1\n", + " while left <= right:\n", + " mid = (left + right) // 2\n", + " if nums[mid] == target:\n", + " result = mid\n", + " left = mid + 1\n", + " elif nums[mid] < target:\n", + " left = mid + 1\n", + " else:\n", + " right = mid - 1\n", + " return result\n", + " \n", + " return [find_first(), find_last()]\n", + "\n", + "print(find_first_last([5,7,7,8,8,10], 8)) # [3, 4]" + ] }, { "cell_type": "markdown", - "id": "cbf4b849", + "id": "42465298", "metadata": {}, "source": [ - "## Index as a hash key\n" + "## Bitwise XOR\n" ] }, { "cell_type": "markdown", - "id": "fd2372f3", + "id": "22b2d840", "metadata": {}, "source": [ - "- If you are given a sequence and the interviewer asks for O(1) space, it might be possible to use the array itself as a hash table.\n", - "- For example, if the array only has values from 1 to N, where N is the length of the array, negate the value at that index (minus one) to indicate presence of that number.\n" + "## Top 'K' Elements\n" ] }, { "cell_type": "code", "execution_count": null, - "id": "4d69a7c2", + "id": "31825f29", "metadata": {}, "outputs": [], - "source": [] + "source": [ + "## Top 'K' Elements\n", + "\n", + "\"\"\"\n", + "### Top 'K' Elements\n", + "\n", + "- Find k largest/smallest elements\n", + "- Use heap: min-heap for k largest, max-heap for k smallest\n", + "- Time: O(n log k), Space: O(k)\n", + "- More efficient than sorting when k << n\n", + "\"\"\"\n", + "\n", + "import heapq\n", + "\n", + "# Example: K Largest Elements\n", + "def find_k_largest(nums: list[int], k: int) -> list[int]:\n", + " return heapq.nlargest(k, nums)\n", + "\n", + "print(find_k_largest([3, 1, 5, 12, 2, 11], 3)) # [12, 11, 5]\n", + "\n", + "# Example: K Closest Points to Origin\n", + "def k_closest_points(points: list[list[int]], k: int) -> list[list[int]]:\n", + " # Min heap of (distance, point)\n", + " min_heap = []\n", + " \n", + " for point in points:\n", + " dist = point[0]**2 + point[1]**2\n", + " heapq.heappush(min_heap, (dist, point))\n", + " \n", + " result = []\n", + " for _ in range(k):\n", + " result.append(heapq.heappop(min_heap)[1])\n", + " \n", + " return result\n", + "\n", + "print(k_closest_points([[1,3],[2,3],[5,-1],[-2,4]], 2))\n", + "# [[1,3],[2,3]]" + ] + }, + { + "cell_type": "markdown", + "id": "95985c70", + "metadata": {}, + "source": [ + "## K-way Merge\n" + ] + }, + { + "cell_type": "markdown", + "id": "8b4536e1", + "metadata": {}, + "source": [ + "## Greedy Algorithms\n" + ] + }, + { + "cell_type": "markdown", + "id": "7b8c7aa5", + "metadata": {}, + "source": [ + "## 0/1 Knapsack\n" + ] + }, + { + "cell_type": "markdown", + "id": "3aded578", + "metadata": {}, + "source": [ + "## Fibonacci Numbers\n" + ] + }, + { + "cell_type": "markdown", + "id": "a09da022", + "metadata": {}, + "source": [ + "## Palindromic Subsequence\n" + ] }, { "cell_type": "markdown", - "id": "6e5a636e", + "id": "7edef10d", "metadata": {}, "source": [ - "## Traversing the array more than once\n" + "## Backtracking\n" ] }, { "cell_type": "markdown", - "id": "55aef46f", + "id": "a13c0398", "metadata": {}, "source": [ - "- This might be obvious, but traversing the array twice/thrice (as long as fewer than n times) is still O(n).\n", - "- Sometimes traversing the array more than once can help you solve the problem while keeping the time complexity to O(n).\n" + "## Trie\n" + ] + }, + { + "cell_type": "markdown", + "id": "aeaef085", + "metadata": {}, + "source": [ + "## Topological Sort\n" + ] + }, + { + "cell_type": "markdown", + "id": "a63bd79b", + "metadata": {}, + "source": [ + "## Union Find\n" + ] + }, + { + "cell_type": "markdown", + "id": "2ce5aaba", + "metadata": {}, + "source": [ + "## Ordered Set\n" + ] + }, + { + "cell_type": "markdown", + "id": "8b3e26a4", + "metadata": {}, + "source": [ + "## Prefix Sum\n" ] }, { "cell_type": "code", "execution_count": null, - "id": "f1faad73", + "id": "0d1e4fbe", "metadata": {}, "outputs": [], - "source": [] + "source": [ + "## Prefix Sum\n", + "\n", + "\"\"\"\n", + "### Prefix Sum\n", + "\n", + "- Precompute cumulative sum up to each index\n", + "- Query range sum in O(1) after O(n) preprocessing\n", + "- prefix_sum[i] = sum of all elements from 0 to i\n", + "- Range sum [i, j] = prefix_sum[j] - prefix_sum[i-1]\n", + "\"\"\"\n", + "\n", + "# Example: Range Sum Query\n", + "class PrefixSumArray:\n", + " def __init__(self, nums: list[int]):\n", + " self.prefix = [0] * (len(nums) + 1)\n", + " for i in range(len(nums)):\n", + " self.prefix[i + 1] = self.prefix[i] + nums[i]\n", + " \n", + " def range_sum(self, left: int, right: int) -> int:\n", + " return self.prefix[right + 1] - self.prefix[left]\n", + "\n", + "nums = [1, 2, 3, 4, 5]\n", + "ps = PrefixSumArray(nums)\n", + "print(ps.range_sum(1, 3)) # 2+3+4 = 9\n", + "\n", + "# Example: Subarray sum equals K\n", + "def subarray_sum(nums: list[int], k: int) -> int:\n", + " count = 0\n", + " prefix_sum = 0\n", + " sum_count = {0: 1} # Map of sum -> frequency\n", + " \n", + " for num in nums:\n", + " prefix_sum += num\n", + " \n", + " # Check if (prefix_sum - k) exists\n", + " if prefix_sum - k in sum_count:\n", + " count += sum_count[prefix_sum - k]\n", + " \n", + " sum_count[prefix_sum] = sum_count.get(prefix_sum, 0) + 1\n", + " \n", + " return count\n", + "\n", + "print(subarray_sum([1, 1, 1], 2)) # 2 (indices [0,1] and [1,2])" + ] + }, + { + "cell_type": "markdown", + "id": "8c644940", + "metadata": {}, + "source": [ + "## Multi-threaded\n" + ] }, { "cell_type": "markdown", @@ -220,7 +988,7 @@ "id": "ea0dcaa5", "metadata": {}, "source": [ - "## Contains Duplicate" + "## Contains Duplicate\n" ] }, { @@ -316,7 +1084,7 @@ "id": "9510f012", "metadata": {}, "source": [ - "## Pangram" + "## Pangram\n" ] }, { @@ -374,7 +1142,6 @@ "\n", "class Solution:\n", " def checkIfPangram(self, sentence):\n", - " # TODO: Write your code here\n", " ref = set(\"abcdefghijklmnopqrstuvwxyz\")\n", " check = set(sentence.lower()) \n", " check = {c for c in check if 'a' <= c <= 'z'}\n", @@ -386,7 +1153,7 @@ "id": "368eb8d1", "metadata": {}, "source": [ - "## Reverse Vowels" + "## Reverse Vowels\n" ] }, { @@ -405,10 +1172,12 @@ "\n", "Input: s= \"hello\"\n", "Output: \"holle\"\n", + "\n", "Example 2:\n", "\n", "Input: s= \"AEIOU\"\n", "Output: \"UOIEA\"\n", + "\n", "Example 3:\n", "\n", "Input: s= \"DesignGUrus\"\n", @@ -421,97 +1190,147 @@ "\n", "# Understanding\n", "# - Reiterate Question + requirements to ensure you understand the problem\n", + "# given a string s we want to reverse every vowel found in that string. Reversing means replacing the current vowel with the first vowel we find at the end of the string\n", + "# the output should also be case sensitive\n", "\n", "# Approach\n", - "# - Describe your approach to solving the problem.\n", + "# - Describe your approach + psuedocode to solving the problem.\n", + "# Brute Force:\n", + "# 1. start and end pointers on the input string\n", + "# 1b. create a string with all vowels lower and upper\n", + "# 2. move pointers only if they are not vowels and they are not equal. \n", + " # a. if both pointers on vowel swap the vowel\n", + " # b. if left pointer on vowel, move right to left until it hits vowel (vice versa)\n", + "# 3. return string\n", + "\n", + "# Code\n", + "# - Code the solution from pseudocode\n", "\n", "# Complexity \n", "# - Add your time complexity here\n", - "\n", + "# O(n) to loop through input string\n", "# - Add your space complexity here\n", + "# O(1) since max 5 vowels to store in set ds\n", "\n", - "# PsuedoCode\n", - "# - Describe solution in psuedocode\n", + "# Optimized:\n", "\n", "class Solution:\n", " def reverseVowels(self, s: str) -> str:\n", - " # TODO: Write your code here\n", - " return s" + " vowels = \"aeiouAEIOU\"\n", + " left = 0\n", + " right = len(s) - 1\n", + " carray = list(s)\n", + " while left < right:\n", + " if carray[left] not in vowels:\n", + " left+=1\n", + " elif carray[right] not in vowels:\n", + " right-=1\n", + " else:\n", + " carray[left],carray[right] = carray[right], carray[left]\n", + " left +=1\n", + " right -=1\n", + " return \"\".join(carray)" ] }, { "cell_type": "markdown", - "id": "7f389c01", + "id": "16b0d82d", "metadata": {}, "source": [ - "## Two Sum" + "## Valid Palindrome\n" ] }, { "cell_type": "code", "execution_count": null, - "id": "baf3270d", + "id": "951ca197", "metadata": {}, "outputs": [], - "source": [] - }, - { - "cell_type": "markdown", - "id": "f07d3b89", - "metadata": {}, "source": [ - "## Best Time to Buy and Sell Stock\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e9a21f97", - "metadata": {}, - "outputs": [ - { - "ename": "NameError", - "evalue": "name 'List' is not defined", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n", - "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)\n", - "Cell \u001b[0;32mIn[1], line 11\u001b[0m\n", - "\u001b[1;32m 2\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n", - "\u001b[1;32m 3\u001b[0m \u001b[38;5;124;03mQ: You are given an array prices where prices[i] is the price of a given stock on the ith day.\u001b[39;00m\n", - "\u001b[1;32m 4\u001b[0m \n", - "\u001b[0;32m (...)\u001b[0m\n", - "\u001b[1;32m 7\u001b[0m \u001b[38;5;124;03mReturn the maximum profit you can achieve from this transaction. If you cannot achieve any profit, return 0.\u001b[39;00m\n", - "\u001b[1;32m 8\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n", - "\u001b[1;32m 10\u001b[0m \u001b[38;5;66;03m#A: \u001b[39;00m\n", - "\u001b[0;32m---> 11\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mmaxProfit\u001b[39m(\u001b[38;5;28mself\u001b[39m, prices: List[\u001b[38;5;28mint\u001b[39m]) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m \u001b[38;5;28mint\u001b[39m:\n", - "\u001b[1;32m 12\u001b[0m \u001b[38;5;28;01mpass\u001b[39;00m\n", - "\n", - "\u001b[0;31mNameError\u001b[0m: name 'List' is not defined" - ] - } - ], - "source": [ - "from typing import List\n", - "\"\"\"\n", - "Q: You are given an array prices where prices[i] is the price of a given stock on the ith day.\n", - "\n", - "You want to maximize your profit by choosing a single day to buy one stock and choosing a different day in the future to sell that stock.\n", - "\n", - "Return the maximum profit you can achieve from this transaction. If you cannot achieve any profit, return 0.\n", - "\"\"\"\n", - "\n", - "#A: \n", - "def maxProfit(self, prices: List[int]) -> int:\n", - " pass" + "\"\"\"\n", + "A phrase is a palindrome if, after converting all uppercase letters into lowercase letters and removing all non-alphanumeric characters, it reads the same forward and backward. Alphanumeric characters include letters and numbers.\n", + "\n", + "Given a string s, return true if it is a palindrome, or false otherwise.\n", + "\n", + "Example 1:\n", + "\n", + "Input: sentence = \"A man, a plan, a canal, Panama!\"\n", + "Output: true\n", + "Explanation: \"amanaplanacanalpanama\" is a palindrome.\n", + "\n", + "Example 2:\n", + "\n", + "Input: sentence = \"Was it a car or a cat I saw?\"\n", + "Output: true\n", + "Explanation: Explanation: \"wasitacaroracatisaw\" is a palindrome.\n", + "Constraints:\n", + "\n", + "1 <= s.length <= 2 * 105\n", + "s consists only of printable ASCII characters.\n", + "\"\"\"\n", + "\n", + "# Understanding:\n", + "# - Reiterate Question + requirements to ensure you understand the problem\n", + "# I want to determine whether an input is a palindrome or not\n", + "# A palindrom is a string where after normalization (removing non-alpha chars and lowercasing chars) it reads the same forward and backward \n", + "\n", + "# Approach:\n", + "# - Describe your approach + pseudocode to solving the problem.\n", + "# Brute Force:\n", + "# I can use .isalpha and .lower to copy only the alpha num chars from the input string into a new string. \n", + "# I can then try using two pointers one at the end and one at beginning to see if the left half equals the right half of the new strings\n", + "# I will implement brute force way first to check if the soltion works and then I will see if there are any optimizations I can make\n", + "\n", + "# Code:\n", + "# - Code solution from psuedocode\n", + "\n", + "# Complexity:\n", + "# - Add your time complexity here\n", + "# - Add your space complexity here\n", + "\n", + "# Optimized:\n", + "\n", + "class Solution:\n", + " def isPalindrome(self, s: str) -> bool:\n", + " # TODO: Write your code here\n", + " cleaned = \"\"\n", + "\n", + " for char in s:\n", + " if char.isalnum():\n", + " cleaned += char.lower()\n", + " \n", + " l = 0\n", + " r = len(cleaned) - 1\n", + " \n", + " while l < r:\n", + " if cleaned[l] != cleaned[r]:\n", + " return False\n", + " l+=1\n", + " r-=1\n", + "\n", + " return True\n" ] } ], "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, "language_info": { - "name": "python" + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.2" } }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file diff --git a/Datastructures + Algorithms/10_graph.ipynb b/Datastructures + Algorithms/10_graph.ipynb deleted file mode 100644 index e69de29..0000000 diff --git a/Datastructures + Algorithms/1a_strings.ipynb b/Datastructures + Algorithms/1a_strings.ipynb new file mode 100644 index 0000000..653f8c8 --- /dev/null +++ b/Datastructures + Algorithms/1a_strings.ipynb @@ -0,0 +1,570 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "f94b0f5f", + "metadata": {}, + "source": [ + "# String Implementations" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "69f278f3", + "metadata": {}, + "outputs": [], + "source": [ + "# Empty string\n", + "s = \"\"\n", + "\n", + "# String with initial value\n", + "s = \"hello\"\n", + "\n", + "# String with escape sequences\n", + "s = \"hello\\nworld\" # newline\n", + "s = \"tab\\there\" # tab\n", + "s = \"quote\\\"here\" # escaped quote\n", + "\n", + "# String from other types\n", + "s = str(42) # \"42\"\n", + "s = str([1, 2, 3]) # \"[1, 2, 3]\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8f6befb1", + "metadata": {}, + "outputs": [], + "source": [ + "## String Immutability\n", + "\n", + "\"\"\"\n", + "### String Immutability\n", + "\n", + "- Strings are immutable in Python\n", + "- Cannot modify individual characters\n", + "- Operations like replace() return new strings\n", + "\"\"\"\n", + "\n", + "s = \"hello\"\n", + "# s[0] = 'H' # TypeError: 'str' object does not support item assignment\n", + "\n", + "# To modify, create new string\n", + "s = \"H\" + s[1:]\n", + "print(s) # 'Hello'\n", + "\n", + "# Or use replace\n", + "s = \"hello\"\n", + "s = s.replace(\"h\", \"H\")\n", + "print(s) # 'Hello'" + ] + }, + { + "cell_type": "markdown", + "id": "450361b2", + "metadata": {}, + "source": [ + "# Methods / Operations" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2f2015ff", + "metadata": {}, + "outputs": [], + "source": [ + "## Indexing & Slicing\n", + "\n", + "# Access character - O(1)\n", + "s = \"hello\"\n", + "print(s[0]) # 'h'\n", + "print(s[1]) # 'e'\n", + "print(s[-1]) # 'o' (last char)\n", + "print(s[-2]) # 'l' (second to last)\n", + "\n", + "# Slice substring - O(k) where k is slice length\n", + "print(s[1:4]) # 'ell' (indices 1,2,3)\n", + "print(s[:3]) # 'hel' (first 3)\n", + "print(s[2:]) # 'llo' (from index 2 to end)\n", + "print(s[::2]) # 'hlo' (every 2nd char)\n", + "print(s[::-1]) # 'olleh' (reversed)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4b86094d", + "metadata": {}, + "outputs": [], + "source": [ + "## .len() / length\n", + "\n", + "\"\"\"\n", + "### len(string)\n", + "\n", + "- Return number of characters in string - O(1)\n", + "\"\"\"\n", + "\n", + "s = \"hello\"\n", + "print(len(s)) # 5\n", + "print(len(\"\")) # 0\n", + "print(len(\"hello world\")) # 11 (includes space)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fd478c50", + "metadata": {}, + "outputs": [], + "source": [ + "## .upper() / .lower() / .title() / .capitalize()\n", + "\n", + "\"\"\"\n", + "### Case Methods\n", + "\n", + "- .upper(): convert to uppercase - O(n)\n", + "- .lower(): convert to lowercase - O(n)\n", + "- .title(): capitalize first letter of each word - O(n)\n", + "- .capitalize(): capitalize first letter only - O(n)\n", + "- .casefold(): aggressive lowercase for comparisons - O(n)\n", + "\"\"\"\n", + "\n", + "s = \"Hello World\"\n", + "print(s.upper()) # 'HELLO WORLD'\n", + "print(s.lower()) # 'hello world'\n", + "print(s.title()) # 'Hello World'\n", + "print(s.capitalize()) # 'Hello world'\n", + "\n", + "# Unicode example\n", + "s = \"Straße\"\n", + "print(s.lower()) # 'straße'\n", + "print(s.casefold()) # 'strasse' (stronger)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "050d10af", + "metadata": {}, + "outputs": [], + "source": [ + "## .strip() / .lstrip() / .rstrip()\n", + "\n", + "\"\"\"\n", + "### Strip Methods\n", + "\n", + "- .strip(): remove leading/trailing whitespace - O(n)\n", + "- .lstrip(): remove leading (left) whitespace - O(n)\n", + "- .rstrip(): remove trailing (right) whitespace - O(n)\n", + "- Can specify characters to strip\n", + "\"\"\"\n", + "\n", + "s = \" hello world \\n\"\n", + "print(f\"'{s.strip()}'\") # 'hello world'\n", + "print(f\"'{s.lstrip()}'\") # 'hello world \\n'\n", + "print(f\"'{s.rstrip()}'\") # ' hello world'\n", + "\n", + "# Strip specific characters\n", + "s = \"~~--data--~~\"\n", + "print(s.strip(\"~-\")) # 'data'\n", + "print(s.lstrip(\"~\")) # '--data--~~'\n", + "print(s.rstrip(\"-~\")) # '~~--data'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "060b7507", + "metadata": {}, + "outputs": [], + "source": [ + "## .split() / .rsplit()\n", + "\n", + "\"\"\"\n", + "### Split Methods\n", + "\n", + "- .split(sep): split by separator (default whitespace) - O(n)\n", + "- .rsplit(sep): split from right - O(n)\n", + "- maxsplit parameter limits splits\n", + "\"\"\"\n", + "\n", + "s = \"a,b,c,d\"\n", + "print(s.split(\",\")) # ['a', 'b', 'c', 'd']\n", + "\n", + "s = \"hello world python\"\n", + "print(s.split()) # ['hello', 'world', 'python']\n", + "\n", + "s = \"a-b-c-d-e\"\n", + "print(s.split(\"-\", maxsplit=2)) # ['a', 'b', 'c-d-e'] (max 2 splits)\n", + "print(s.rsplit(\"-\", maxsplit=1)) # ['a-b-c-d', 'e'] (split from right)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4d64ff7c", + "metadata": {}, + "outputs": [], + "source": [ + "## .join()\n", + "\n", + "\"\"\"\n", + "### .join()\n", + "\n", + "- Join iterable with string as separator - O(n)\n", + "- More efficient than + for multiple strings\n", + "\"\"\"\n", + "\n", + "words = ['hello', 'world', 'python']\n", + "s = \" \".join(words)\n", + "print(s) # 'hello world python'\n", + "\n", + "chars = ['a', 'b', 'c']\n", + "s = \"-\".join(chars)\n", + "print(s) # 'a-b-c'\n", + "\n", + "# Efficient for building strings\n", + "result = \"\".join(['a', 'b', 'c'])\n", + "print(result) # 'abc'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0b59ae15", + "metadata": {}, + "outputs": [], + "source": [ + "## .find() / .rfind() / .index()\n", + "\n", + "\"\"\"\n", + "### Search Methods\n", + "\n", + "- .find(sub): return index of substring, -1 if not found - O(n*m)\n", + "- .rfind(sub): find from right - O(n*m)\n", + "- .index(sub): like find but raises ValueError if not found - O(n*m)\n", + "\"\"\"\n", + "\n", + "s = \"hello world hello\"\n", + "print(s.find(\"o\")) # 4 (first 'o')\n", + "print(s.rfind(\"o\")) # 14 (last 'o')\n", + "print(s.find(\"world\")) # 6\n", + "print(s.find(\"xyz\")) # -1 (not found)\n", + "\n", + "# index raises error\n", + "print(s.index(\"world\")) # 6\n", + "try:\n", + " s.index(\"xyz\")\n", + "except ValueError:\n", + " print(\"Substring not found\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "29c49363", + "metadata": {}, + "outputs": [], + "source": [ + "## .count()\n", + "\n", + "\"\"\"\n", + "### .count(sub)\n", + "\n", + "- Count non-overlapping occurrences of substring - O(n)\n", + "\"\"\"\n", + "\n", + "s = \"hello world hello\"\n", + "print(s.count(\"hello\")) # 2\n", + "print(s.count(\"l\")) # 3\n", + "print(s.count(\"o\")) # 2\n", + "print(s.count(\"xyz\")) # 0\n", + "\n", + "# With overlapping (count is non-overlapping)\n", + "s = \"aaa\"\n", + "print(s.count(\"aa\")) # 1 (non-overlapping)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "06f2aa1b", + "metadata": {}, + "outputs": [], + "source": [ + "## .replace()\n", + "\n", + "\"\"\"\n", + "### .replace(old, new, count)\n", + "\n", + "- Replace occurrences of substring - O(n)\n", + "- count parameter limits replacements\n", + "\"\"\"\n", + "\n", + "s = \"hello world hello\"\n", + "print(s.replace(\"hello\", \"hi\")) # 'hi world hi'\n", + "print(s.replace(\"hello\", \"hi\", 1)) # 'hi world hello' (1st only)\n", + "print(s.replace(\"o\", \"0\")) # 'hell0 w0rld hell0'\n", + "\n", + "# Empty string replacement\n", + "s = \"a,b,c\"\n", + "print(s.replace(\",\", \"\")) # 'abc'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "da214726", + "metadata": {}, + "outputs": [], + "source": [ + "## .startswith() / .endswith()\n", + "\n", + "\"\"\"\n", + "### Prefix & Suffix Methods\n", + "\n", + "- .startswith(prefix): check if starts with prefix - O(m) where m is prefix length\n", + "- .endswith(suffix): check if ends with suffix - O(m)\n", + "- Can accept tuple of prefixes/suffixes\n", + "\"\"\"\n", + "\n", + "s = \"hello world\"\n", + "print(s.startswith(\"hello\")) # True\n", + "print(s.startswith(\"bye\")) # False\n", + "print(s.endswith(\"world\")) # True\n", + "print(s.endswith(\"xyz\")) # False\n", + "\n", + "# Multiple prefixes\n", + "print(s.startswith((\"hello\", \"hi\"))) # True\n", + "print(s.endswith((\".txt\", \".py\"))) # False\n", + "\n", + "# Useful for file checks\n", + "filename = \"document.pdf\"\n", + "print(filename.endswith((\".pdf\", \".doc\", \".docx\"))) # True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "11bfdfc2", + "metadata": {}, + "outputs": [], + "source": [ + "## .isalpha() / .isdigit() / .isalnum() / .isspace()\n", + "\n", + "\"\"\"\n", + "### Character Check Methods\n", + "\n", + "- .isalpha(): all characters are alphabetic - O(n)\n", + "- .isdigit(): all characters are digits - O(n)\n", + "- .isalnum(): all characters are alphanumeric - O(n)\n", + "- .isspace(): all characters are whitespace - O(n)\n", + "- .isdecimal(): all characters are decimal digits - O(n)\n", + "- .isnumeric(): all characters are numeric - O(n)\n", + "- .istitle(): first letter of each word is uppercase - O(n)\n", + "\"\"\"\n", + "\n", + "print(\"hello\".isalpha()) # True\n", + "print(\"hello123\".isalpha()) # False\n", + "print(\"hello123\".isalnum()) # True\n", + "\n", + "print(\"12345\".isdigit()) # True\n", + "print(\"12.45\".isdigit()) # False (has dot)\n", + "\n", + "print(\" \\n\\t\".isspace()) # True\n", + "print(\"hello world\".isspace()) # False\n", + "\n", + "print(\"Hello World\".istitle()) # True\n", + "print(\"hello world\".istitle()) # False\n", + "\n", + "# Unicode digits nuance\n", + "print(\"Ⅳ\".isnumeric()) # True (Roman numeral)\n", + "print(\"١٢٣\".isdecimal()) # True (Arabic-Indic digits)\n", + "print(\"١٢٣\".isdigit()) # True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b7512a45", + "metadata": {}, + "outputs": [], + "source": [ + "## .in operator\n", + "\n", + "\"\"\"\n", + "### Substring Check (in)\n", + "\n", + "- Check if substring exists - O(n*m) worst case, O(n) average\n", + "- Returns True/False\n", + "\"\"\"\n", + "\n", + "s = \"hello world\"\n", + "print(\"hello\" in s) # True\n", + "print(\"world\" in s) # True\n", + "print(\"xyz\" in s) # False\n", + "print(\"\" in s) # True (empty string always in)\n", + "\n", + "# Case sensitive\n", + "print(\"Hello\" in s) # False\n", + "print(\"HELLO\" in s) # False\n", + "\n", + "# Useful for validation\n", + "if \"error\" in result:\n", + " print(\"Something went wrong\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d0e949df", + "metadata": {}, + "outputs": [], + "source": [ + "## String Formatting\n", + "\n", + "\"\"\"\n", + "### String Formatting Methods\n", + "\n", + "- f-strings (f\"...\"): most modern and efficient - O(n)\n", + "- .format(): older method - O(n)\n", + "- % operator: oldest method - O(n)\n", + "\"\"\"\n", + "\n", + "# f-strings (recommended)\n", + "name = \"Alice\"\n", + "age = 30\n", + "print(f\"Name: {name}, Age: {age}\") # 'Name: Alice, Age: 30'\n", + "print(f\"Calculation: {2 + 3}\") # 'Calculation: 5'\n", + "print(f\"Name: {name.upper()}\") # 'Name: ALICE'\n", + "\n", + "# Format specifications\n", + "value = 3.14159\n", + "print(f\"{value:.2f}\") # '3.14' (2 decimals)\n", + "print(f\"{42:05d}\") # '00042' (zero padded)\n", + "\n", + "# .format() method\n", + "print(\"Name: {}, Age: {}\".format(name, age))\n", + "print(\"Name: {0}, Age: {1}\".format(name, age))\n", + "\n", + "# % operator (old style, avoid)\n", + "print(\"Name: %s, Age: %d\" % (name, age))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4653f84f", + "metadata": {}, + "outputs": [], + "source": [ + "## Multiline Strings\n", + "\n", + "\"\"\"\n", + "### Multiline Strings\n", + "\n", + "- Triple-quoted strings for multiline content\n", + "- Preserves newlines\n", + "\"\"\"\n", + "\n", + "# Multiline string\n", + "text = \"\"\"\n", + "This is a multiline\n", + "string that spans\n", + "multiple lines.\n", + "\"\"\"\n", + "print(text)\n", + "\n", + "# Useful for docstrings\n", + "def my_function():\n", + " \"\"\"\n", + " This is a docstring.\n", + " \n", + " It can span multiple lines.\n", + " \"\"\"\n", + " pass\n", + "\n", + "# Can use \\ to continue line without newline\n", + "text = \"This is a very long string that \" \\\n", + " \"continues on the next line\"\n", + "print(text)" + ] + }, + { + "cell_type": "markdown", + "id": "8a353d21", + "metadata": {}, + "source": [ + "# Corner Cases TODO add examples for each case" + ] + }, + { + "cell_type": "markdown", + "id": "d4e7b02a", + "metadata": {}, + "source": [ + "- Empty array []\n", + "- Single or two elements\n", + "- All equal values (ties!)\n", + "- Already sorted vs reverse sorted\n", + "- Large values / potential overflow (use Python big ints, but be mindful)\n", + "- Negative numbers / zeros (esp. in products, prefix sums, Kadane)\n", + "- Duplicates (affects two-sum, set logic, binary search bounds)\n", + "- Off-by-one in slicing (half-open ranges [l, r) vs closed)\n", + "- In-place updates while iterating (iterate on indices or a copy)" + ] + }, + { + "cell_type": "markdown", + "id": "f6005fda", + "metadata": {}, + "source": [ + "# Techniques" + ] + }, + { + "cell_type": "markdown", + "id": "dc1eed72", + "metadata": {}, + "source": [ + "- fill in as you encounter through problem solving" + ] + }, + { + "cell_type": "markdown", + "id": "5843d284", + "metadata": {}, + "source": [ + "# Practice Projects" + ] + }, + { + "cell_type": "markdown", + "id": "36f82801", + "metadata": {}, + "source": [ + "- use this to practice multiple techniques + operations in the form of a project. Try to recall everything from memory before looking up\n", + "- create another ipynb notebook with the same format as this for the project" + ] + }, + { + "cell_type": "markdown", + "id": "05b965d1", + "metadata": {}, + "source": [ + "- Example projects TODO" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Datastructures + Algorithms/1a_lists_strings.ipynb b/Datastructures + Algorithms/1b_lists.ipynb similarity index 55% rename from Datastructures + Algorithms/1a_lists_strings.ipynb rename to Datastructures + Algorithms/1b_lists.ipynb index 3b90d32..c2ad4a8 100644 --- a/Datastructures + Algorithms/1a_lists_strings.ipynb +++ b/Datastructures + Algorithms/1b_lists.ipynb @@ -5,7 +5,7 @@ "id": "9db9da09", "metadata": {}, "source": [ - "# Implementations" + "# List Implementations" ] }, { @@ -64,22 +64,6 @@ "# Methods / Operations" ] }, - { - "cell_type": "markdown", - "id": "5e494587", - "metadata": {}, - "source": [ - "## Access / Indexing" - ] - }, - { - "cell_type": "markdown", - "id": "cb156c05", - "metadata": {}, - "source": [ - "- Access an element from the list" - ] - }, { "cell_type": "code", "execution_count": null, @@ -98,11 +82,7 @@ "# Indexing\n", "\n", "fruits = ['orange', 'apple', 'pear', 'banana', 'kiwi', 'apple']\n", - "print(fruits[3])\n", - "\n", - "word = 'Python'\n", - "print(word[0])\n", - "print(word[5])" + "print(fruits[3])" ] }, { @@ -122,24 +102,37 @@ "source": [ "# Negative index\n", "\n", - "print(fruits[-1])\n", - "print(word[-1])" + "print(fruits[-1])" ] }, { - "cell_type": "markdown", - "id": "491d66d5", - "metadata": {}, - "source": [ - "## Slicing " - ] - }, - { - "cell_type": "markdown", - "id": "df8bcc0b", + "cell_type": "code", + "execution_count": null, + "id": "eedaff53", "metadata": {}, + "outputs": [], "source": [ - "- TODO" + "# .pop([i])\n", + "\n", + "\"\"\"\n", + "### .pop([i])\n", + "\n", + "- Remove and return item at index i (default last)\n", + "- Raises IndexError if list is empty\n", + "- O(1) if removing last element, O(n) otherwise\n", + "\"\"\"\n", + "\n", + "fruits = ['orange', 'apple', 'pear', 'banana', 'kiwi', 'apple']\n", + "\n", + "# Pop last item - O(1)\n", + "last = fruits.pop()\n", + "print(f\"Popped: {last}\") # 'apple'\n", + "print(fruits) # ['orange', 'apple', 'pear', 'banana', 'kiwi']\n", + "\n", + "# Pop specific index - O(n)\n", + "second = fruits.pop(1)\n", + "print(f\"Popped at index 1: {second}\") # 'apple'\n", + "print(fruits) # ['orange', 'pear', 'banana', 'kiwi']" ] }, { @@ -155,41 +148,12 @@ "print(fruits[1:4]) # ['apple','pear','banana']\n", "print(fruits[:3]) # first 3\n", "print(fruits[::2]) # step 2\n", - "print(fruits[::-1]) # reversed copy\n", - "\n", - "word = 'Savestate'\n", - "word[0:2]\n", - "\n", - "# character from the beginning to position 2 (excluded)\n", - "print(word[:2])\n", - "\n", - "# characters from position 4 (included) to the end\n", - "print(word[4:])\n", - "\n", - "# characters from the second-last to the end\n", - "print(word[-2:])" - ] - }, - { - "cell_type": "markdown", - "id": "da0d5ace", - "metadata": {}, - "source": [ - "## .append(x)" - ] - }, - { - "cell_type": "markdown", - "id": "00fe156c", - "metadata": {}, - "source": [ - " - Add an item to the end of a list\n", - " - Similar to a[len(a):] = [x]" + "print(fruits[::-1]) # reversed copy" ] }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "id": "e43984ef", "metadata": {}, "outputs": [ @@ -202,32 +166,21 @@ } ], "source": [ + "## .append(x)\n", + "\n", + "\"\"\"\n", + " - Add an item to the end of a list\n", + " - Similar to a[len(a):] = [x]\n", + "\"\"\"\n", "# append to end - O(1)\n", "\n", "fruits.append('grape')\n", "print(fruits)" ] }, - { - "cell_type": "markdown", - "id": "477271f5", - "metadata": {}, - "source": [ - "## .extend(iterable)" - ] - }, - { - "cell_type": "markdown", - "id": "e239c7de", - "metadata": {}, - "source": [ - "- Extend the list by appending all the items from the iterable. \n", - "- Similar to a[len(a):] = iterable." - ] - }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "id": "5f9c5d38", "metadata": {}, "outputs": [ @@ -240,28 +193,18 @@ } ], "source": [ + "## .extend(iterable)\n", + "\n", + "\"\"\"\n", + "- Extend the list by appending all the items from the iterable. \n", + "- Similar to a[len(a):] = iterable.\n", + "\"\"\"\n", "# Concatenate many at once - O(k)\n", + "\n", "fruits.extend(['x','y'])\n", "print(fruits)" ] }, - { - "cell_type": "markdown", - "id": "43e341c0", - "metadata": {}, - "source": [ - "## .insert(i,x)" - ] - }, - { - "cell_type": "markdown", - "id": "2c3d8db5", - "metadata": {}, - "source": [ - "- Insert an item at a given position in a list.\n", - "- i - index of the element before which to insert\n" - ] - }, { "cell_type": "code", "execution_count": null, @@ -277,6 +220,12 @@ } ], "source": [ + "## .insert(i,x)\n", + "\n", + "\"\"\"\n", + "- Insert an item at a given position in a list.\n", + "- i - index of the element before which to insert\n", + "\"\"\"\n", "# append to front - O(n)\n", "\n", "fruits.insert(0,'passionfruit')\n", @@ -301,7 +250,7 @@ "# append within the list - O(n) since have to shift all elements to the right to maintain size of array\n", "\n", "fruits.insert(2,'plum')\n", - "print(fruits)\n" + "print(fruits)" ] }, { @@ -325,22 +274,6 @@ "print(fruits)" ] }, - { - "cell_type": "markdown", - "id": "5b55a4b0", - "metadata": {}, - "source": [ - "## .remove(x)" - ] - }, - { - "cell_type": "markdown", - "id": "103c5591", - "metadata": {}, - "source": [ - "- Remove the first item from the list whose value is equal to x" - ] - }, { "cell_type": "code", "execution_count": null, @@ -348,6 +281,12 @@ "metadata": {}, "outputs": [], "source": [ + "## .remove(x)\n", + "\n", + "\"\"\"\n", + "## .remove(x)\n", + "- Remove the first item from the list whose value is equal to x\n", + "\"\"\"\n", "# remove within list - O(n) since have to shift all elements to left to maintain size of array\n", "# remove at end - O(1)\n", "\n", @@ -355,212 +294,230 @@ "print(fruits)" ] }, - { - "cell_type": "markdown", - "id": "fbd3bbdc", - "metadata": {}, - "source": [ - "## .index(x)" - ] - }, - { - "cell_type": "markdown", - "id": "07719831", - "metadata": {}, - "source": [ - "- Return zero-based index of the first occurrence of x in the list. \n", - "- Raises a ValueError if there is no such item." - ] - }, { "cell_type": "code", "execution_count": null, - "id": "118ab094", + "id": "afd89787", "metadata": {}, "outputs": [], "source": [ - "print(fruits.index('pear'))\n", - "print(fruits.index('banana'))" - ] - }, - { - "cell_type": "markdown", - "id": "f5a60617", - "metadata": {}, - "source": [ - "## .strip()" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "b4a32b5a", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "hello\n", - "hello \n", - "\n", - " \t hello\n", - "data\n" - ] - } - ], - "source": [ + "## .clear()\n", + "\n", "\"\"\"\n", - "### .strip() / .lstrip() / .rstrip()\n", + "### .clear()\n", "\n", - "- Remove leading/trailing whitespace by default.\n", - "- Provide a character set to strip specific chars.\n", + "- Remove all items from the list\n", + "- List becomes empty but still exists\n", + "- O(n)\n", "\"\"\"\n", - "# (Code)\n", - "t = \" \\t hello \\n\"\n", - "print(t.strip()) # 'hello'\n", - "print(t.lstrip()) # 'hello \\n'\n", - "print(t.rstrip()) # ' \\t hello'\n", - "u = \"~~--data--~~\"\n", - "print(u.strip(\"~-\")) # 'data--' (strips any of '~' or '-' from ends)" - ] - }, - { - "cell_type": "markdown", - "id": "70d7e1f7", - "metadata": {}, - "source": [ - "## .split()" + "\n", + "fruits = ['orange', 'apple', 'pear']\n", + "fruits.clear()\n", + "print(fruits) # []\n", + "print(len(fruits)) # 0" ] }, { "cell_type": "code", "execution_count": null, - "id": "e9b0401c", + "id": "b916dba9", "metadata": {}, "outputs": [], "source": [ + "## .count(x)\n", + "\n", "\"\"\"\n", - "### .split() / .rsplit()\n", + "### .count(x)\n", "\n", - "- split() with no sep splits on any whitespace and collapses runs.\n", - "- sep specifies exact delimiter; maxsplit controls number of splits.\n", - "- rsplit() splits from the right.\n", + "- Return number of times x appears in the list\n", + "- O(n) - must scan entire list\n", "\"\"\"\n", "\n", - "line = \"a b\\tc\\n\\n d\"\n", - "print(line.split()) # ['a', 'b', 'c', 'd']\n", - "csv = \"a,b,c,d\"\n", - "print(csv.split(\",\")) # ['a', 'b', 'c', 'd']\n", - "print(csv.split(\",\", maxsplit=2)) # ['a', 'b', 'c,d']\n", - "print(csv.rsplit(\",\", maxsplit=1)) # ['a,b,c', 'd']" + "fruits = ['apple', 'banana', 'apple', 'cherry', 'apple']\n", + "count_apples = fruits.count('apple')\n", + "print(f\"Count of 'apple': {count_apples}\") # 3\n", + "\n", + "nums = [1, 2, 2, 3, 2, 4]\n", + "print(f\"Count of 2: {nums.count(2)}\") # 3\n", + "print(f\"Count of 5: {nums.count(5)}\") # 0 (not found)" ] }, { - "cell_type": "markdown", - "id": "2e99d434", + "cell_type": "code", + "execution_count": null, + "id": "118ab094", "metadata": {}, + "outputs": [], "source": [ - "## Case Methods" + "## .index(x)\n", + "\n", + "\"\"\"\n", + "## .index(x)\n", + "- Return zero-based index of the first occurrence of x in the list. \n", + "- Raises a ValueError if there is no such item.\n", + "\"\"\"\n", + "print(fruits.index('pear'))\n", + "print(fruits.index('banana'))" ] }, { "cell_type": "code", "execution_count": null, - "id": "e5e96bc1", + "id": "cf076a18", "metadata": {}, "outputs": [], "source": [ + "## .sort()\n", + "\n", "\"\"\"\n", - "## Case Methods\n", + "### .sort()\n", "\n", - "- .upper(), .lower(), .title(), .capitalize(), .casefold() (stronger lower for caseless matching).\n", + "- Sort list in-place in ascending order\n", + "- Optional: reverse=True for descending, key=func for custom sorting\n", + "- O(n log n) - uses Timsort algorithm\n", + "- Modifies original list\n", "\"\"\"\n", - "# (Code)\n", - "s = \"Straße\"\n", - "print(s.lower()) # 'straße'\n", - "print(s.casefold()) # 'strasse' (better for comparisons)\n", - "print(\"hello world\".title()) # 'Hello World'\n", - "print(\"hello world\".capitalize())# 'Hello world'" - ] - }, - { - "cell_type": "markdown", - "id": "d3b693ff", - "metadata": {}, - "source": [ - "## Count & Predicates" + "\n", + "nums = [3, 1, 4, 1, 5, 9, 2, 6]\n", + "nums.sort()\n", + "print(f\"Sorted: {nums}\") # [1, 1, 2, 3, 4, 5, 6, 9]\n", + "\n", + "# Sort descending\n", + "nums = [3, 1, 4, 1, 5, 9]\n", + "nums.sort(reverse=True)\n", + "print(f\"Descending: {nums}\") # [9, 5, 4, 3, 1, 1]\n", + "\n", + "# Sort by custom key\n", + "words = ['banana', 'pie', 'Washington', 'book']\n", + "words.sort(key=str.lower) # case-insensitive\n", + "print(f\"Case-insensitive: {words}\")\n", + "\n", + "# Sort by length\n", + "words.sort(key=len)\n", + "print(f\"By length: {words}\")\n", + "\n", + "# Sort tuples by second element\n", + "pairs = [(1, 'b'), (3, 'a'), (2, 'c')]\n", + "pairs.sort(key=lambda x: x[1])\n", + "print(f\"By second element: {pairs}\") # [(3, 'a'), (1, 'b'), (2, 'c')]" ] }, { "cell_type": "code", "execution_count": null, - "id": "aa5e05f7", + "id": "3c0e3cfd", "metadata": {}, "outputs": [], "source": [ + "## sorted()\n", + "\n", "\"\"\"\n", - "## Count & Predicates\n", + "### sorted()\n", "\n", - "- .count(sub) occurrences.\n", - "- .isalpha(), .isalnum(), .isdigit(), .isdecimal(), .isnumeric(), .isspace(), .istitle().\n", + "- Return NEW sorted list (doesn't modify original)\n", + "- Works on any iterable\n", + "- Same parameters as .sort(): reverse, key\n", + "- O(n log n)\n", "\"\"\"\n", - "# (Code)\n", - "print(\"mississippi\".count(\"ss\")) # 2\n", - "print(\"abc\".isalpha(), \"abc123\".isalnum(), \"123\".isdigit())\n", - "print(\"Ⅳ\".isnumeric(), \"١٢٣\".isdecimal()) # True False (unicode digits nuance)\n", - "print(\"Hello World\".istitle()) # True" - ] - }, - { - "cell_type": "markdown", - "id": "2bec3dc6", - "metadata": {}, - "source": [ - "## startswith() / endswith()" + "\n", + "nums = [3, 1, 4, 1, 5, 9]\n", + "sorted_nums = sorted(nums)\n", + "print(f\"Original: {nums}\") # [3, 1, 4, 1, 5, 9] - unchanged\n", + "print(f\"Sorted: {sorted_nums}\") # [1, 1, 3, 4, 5, 9]\n", + "\n", + "# Works on any iterable\n", + "print(sorted(\"hello\")) # ['e', 'h', 'l', 'l', 'o']\n", + "print(sorted({3, 1, 2})) # [1, 2, 3]\n", + "\n", + "# Descending\n", + "print(sorted(nums, reverse=True)) # [9, 5, 4, 3, 1, 1]" ] }, { "cell_type": "code", "execution_count": null, - "id": "6ad444d5", + "id": "8418a7b6", "metadata": {}, "outputs": [], "source": [ + "## max() / min() / sum()\n", + "\n", "\"\"\"\n", - "## startswith() / endswith()\n", + "### max() / min() / sum()\n", "\n", - "- Accept tuple of prefixes/suffixes.\n", + "- max(list): largest element - O(n)\n", + "- min(list): smallest element - O(n)\n", + "- sum(list): sum of all elements - O(n)\n", + "- Optional key parameter for custom comparison\n", "\"\"\"\n", - "# (Code)\n", - "fn = \"report.csv\"\n", - "print(fn.endswith((\".csv\", \".tsv\"))) # True\n", - "print(\"http://example.com\".startswith((\"http://\", \"https://\"))) # True" - ] - }, - { - "cell_type": "markdown", - "id": "b748f251", - "metadata": {}, - "source": [ - "## partition() / rpartition()" + "\n", + "nums = [3, 1, 4, 1, 5, 9, 2, 6]\n", + "\n", + "print(f\"Max: {max(nums)}\") # 9\n", + "print(f\"Min: {min(nums)}\") # 1\n", + "print(f\"Sum: {sum(nums)}\") # 31\n", + "print(f\"Average: {sum(nums) / len(nums)}\") # 3.875\n", + "\n", + "# With key parameter\n", + "words = ['apple', 'pie', 'zoo', 'a']\n", + "longest = max(words, key=len)\n", + "shortest = min(words, key=len)\n", + "print(f\"Longest: {longest}\") # 'apple'\n", + "print(f\"Shortest: {shortest}\") # 'a'\n", + "\n", + "# With objects\n", + "students = [\n", + " {'name': 'Alice', 'score': 85},\n", + " {'name': 'Bob', 'score': 92},\n", + " {'name': 'Charlie', 'score': 78}\n", + "]\n", + "top_student = max(students, key=lambda x: x['score'])\n", + "print(f\"Top student: {top_student['name']}\") # 'Bob'" ] }, { "cell_type": "code", "execution_count": null, - "id": "ed246346", + "id": "bc006c03", "metadata": {}, "outputs": [], "source": [ + "## all() / any()\n", + "\n", "\"\"\"\n", - "## partition() / rpartition()\n", + "### all() / any()\n", "\n", - "- Split into (head, sep, tail) at first/last occurrence; always returns 3-tuple.\n", + "- all(iterable): True if all elements are truthy - O(n)\n", + "- any(iterable): True if any element is truthy - O(n)\n", + "- all([]) returns True (vacuous truth)\n", + "- any([]) returns False\n", "\"\"\"\n", - "# (Code)\n", - "print(\"key=value\".partition(\"=\")) # ('key', '=', 'value')\n", - "print(\"a=b=c\".rpartition(\"=\")) # ('a=b', '=', 'c')" + "\n", + "# all() examples\n", + "nums = [2, 4, 6, 8]\n", + "print(all(x % 2 == 0 for x in nums)) # True (all even)\n", + "\n", + "nums = [2, 4, 5, 8]\n", + "print(all(x % 2 == 0 for x in nums)) # False (5 is odd)\n", + "\n", + "# any() examples\n", + "nums = [1, 3, 5, 7]\n", + "print(any(x % 2 == 0 for x in nums)) # False (none even)\n", + "\n", + "nums = [1, 3, 4, 7]\n", + "print(any(x % 2 == 0 for x in nums)) # True (4 is even)\n", + "\n", + "# Real-world use\n", + "users = [\n", + " {'name': 'Alice', 'active': True},\n", + " {'name': 'Bob', 'active': False},\n", + " {'name': 'Charlie', 'active': False}\n", + "]\n", + "has_active = any(user['active'] for user in users)\n", + "print(f\"Any active users: {has_active}\") # True\n", + "\n", + "all_active = all(user['active'] for user in users)\n", + "print(f\"All active: {all_active}\") # False" ] }, { diff --git a/Datastructures + Algorithms/1b_sets.ipynb b/Datastructures + Algorithms/1c_sets.ipynb similarity index 87% rename from Datastructures + Algorithms/1b_sets.ipynb rename to Datastructures + Algorithms/1c_sets.ipynb index abe45b9..d02693e 100644 --- a/Datastructures + Algorithms/1b_sets.ipynb +++ b/Datastructures + Algorithms/1c_sets.ipynb @@ -65,22 +65,6 @@ "# Methods / Operations" ] }, - { - "cell_type": "markdown", - "id": "b682a1d6", - "metadata": {}, - "source": [ - "- Access an element from the array" - ] - }, - { - "cell_type": "markdown", - "id": "daea8755", - "metadata": {}, - "source": [ - "## .add(x)" - ] - }, { "cell_type": "code", "execution_count": null, @@ -88,6 +72,8 @@ "metadata": {}, "outputs": [], "source": [ + "## .add(x)\n", + "\n", "# Add single element - O(1)\n", "fruits = {'apple', 'banana', 'cherry'}\n", "fruits.add('orange')\n", @@ -97,14 +83,6 @@ "print(fruits)" ] }, - { - "cell_type": "markdown", - "id": "88b14760", - "metadata": {}, - "source": [ - "## .update(x)" - ] - }, { "cell_type": "code", "execution_count": null, @@ -112,6 +90,8 @@ "metadata": {}, "outputs": [], "source": [ + "## .update(x)\n", + "\n", "# Add multiple elements - O(k)\n", "fruits.update(['mango', 'grape'])\n", "print(fruits)\n", @@ -121,14 +101,6 @@ "print(fruits)" ] }, - { - "cell_type": "markdown", - "id": "beb13645", - "metadata": {}, - "source": [ - "## .remove(x)" - ] - }, { "cell_type": "code", "execution_count": null, @@ -136,6 +108,8 @@ "metadata": {}, "outputs": [], "source": [ + "## .remove(x)\n", + "\n", "\"\"\"\n", "### .remove(x)\n", "\n", @@ -151,14 +125,6 @@ "# fruits.remove('grape') # KeyError: 'grape'" ] }, - { - "cell_type": "markdown", - "id": "dea6410d", - "metadata": {}, - "source": [ - "## .discard(x)" - ] - }, { "cell_type": "code", "execution_count": null, @@ -166,6 +132,8 @@ "metadata": {}, "outputs": [], "source": [ + "## .discard(x)\n", + "\n", "\"\"\"\n", "### .discard(x)\n", "\n", @@ -182,14 +150,6 @@ "print(fruits) # {'apple', 'cherry'}" ] }, - { - "cell_type": "markdown", - "id": "5f42fda1", - "metadata": {}, - "source": [ - "## .pop()" - ] - }, { "cell_type": "code", "execution_count": null, @@ -197,6 +157,8 @@ "metadata": {}, "outputs": [], "source": [ + "## .pop()\n", + "\n", "\"\"\"\n", "### .pop()\n", "\n", @@ -214,17 +176,9 @@ "# fruits.pop() # KeyError: 'pop from an empty set'" ] }, - { - "cell_type": "markdown", - "id": "bc3fee5e", - "metadata": {}, - "source": [ - "## .clear()" - ] - }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "id": "9b570bce", "metadata": {}, "outputs": [ @@ -238,6 +192,8 @@ } ], "source": [ + "## .clear()\n", + "\n", "\"\"\"\n", "### .clear()\n", "\n", @@ -251,14 +207,6 @@ "print(len(fruits)) # 0" ] }, - { - "cell_type": "markdown", - "id": "994e0c62", - "metadata": {}, - "source": [ - "## in / not in" - ] - }, { "cell_type": "code", "execution_count": null, @@ -266,6 +214,7 @@ "metadata": {}, "outputs": [], "source": [ + "## in / not in\n", "\"\"\"\n", "### in / not in\n", "\n", @@ -283,14 +232,6 @@ "print('apple' in fruits_list) # True, but O(n)" ] }, - { - "cell_type": "markdown", - "id": "5863a494", - "metadata": {}, - "source": [ - "## .union()" - ] - }, { "cell_type": "code", "execution_count": null, @@ -298,6 +239,7 @@ "metadata": {}, "outputs": [], "source": [ + "## .union()\n", "\"\"\"\n", "### .union() / |\n", "\n", @@ -321,14 +263,6 @@ "print(union3) # {1, 2, 3, 4, 5, 6}" ] }, - { - "cell_type": "markdown", - "id": "448d41d1", - "metadata": {}, - "source": [ - "## .intersection()" - ] - }, { "cell_type": "code", "execution_count": null, @@ -336,6 +270,7 @@ "metadata": {}, "outputs": [], "source": [ + "## .intersection()\n", "\"\"\"\n", "### .intersection() / &\n", "\n", @@ -359,14 +294,6 @@ "print(intersection3) # {4}" ] }, - { - "cell_type": "markdown", - "id": "0de58e7c", - "metadata": {}, - "source": [ - "## .difference()" - ] - }, { "cell_type": "code", "execution_count": null, @@ -374,6 +301,7 @@ "metadata": {}, "outputs": [], "source": [ + "## .difference()\n", "\"\"\"\n", "### .difference() / -\n", "\n", @@ -401,14 +329,6 @@ "print(diff4) # {1}" ] }, - { - "cell_type": "markdown", - "id": "878fb1a9", - "metadata": {}, - "source": [ - "## .symmetric_difference() / ^" - ] - }, { "cell_type": "code", "execution_count": null, @@ -416,6 +336,7 @@ "metadata": {}, "outputs": [], "source": [ + "## .symmetric_difference() / ^\n", "\"\"\"\n", "### .symmetric_difference() / ^\n", "\n", @@ -437,14 +358,6 @@ "print(set1 ^ set2 == set2 ^ set1) # True" ] }, - { - "cell_type": "markdown", - "id": "2a373278", - "metadata": {}, - "source": [ - "## .issubset() / <=" - ] - }, { "cell_type": "code", "execution_count": null, @@ -452,6 +365,7 @@ "metadata": {}, "outputs": [], "source": [ + "## .issubset() / <=\n", "\"\"\"\n", "### .issubset() / <=\n", "\n", @@ -473,14 +387,6 @@ "print(set().issubset(set1)) # True" ] }, - { - "cell_type": "markdown", - "id": "1b19f1a8", - "metadata": {}, - "source": [ - "## .issuperset() / >=" - ] - }, { "cell_type": "code", "execution_count": null, @@ -488,6 +394,7 @@ "metadata": {}, "outputs": [], "source": [ + "## .issuperset() / >=\n", "\"\"\"\n", "### .issuperset() / >=\n", "\n", @@ -508,14 +415,6 @@ "print(set1.issuperset(set())) # True" ] }, - { - "cell_type": "markdown", - "id": "db339c1b", - "metadata": {}, - "source": [ - "## .isdisjoint()" - ] - }, { "cell_type": "code", "execution_count": null, @@ -523,6 +422,7 @@ "metadata": {}, "outputs": [], "source": [ + "## .isdisjoint()\n", "\"\"\"\n", "### .isdisjoint()\n", "\n", diff --git a/Datastructures + Algorithms/1c_tuples.ipynb b/Datastructures + Algorithms/1d_tuples.ipynb similarity index 100% rename from Datastructures + Algorithms/1c_tuples.ipynb rename to Datastructures + Algorithms/1d_tuples.ipynb diff --git a/Datastructures + Algorithms/1e_hash_table_dictionary_hash_map.ipynb b/Datastructures + Algorithms/1e_hash_table_dictionary_hash_map.ipynb new file mode 100644 index 0000000..f916817 --- /dev/null +++ b/Datastructures + Algorithms/1e_hash_table_dictionary_hash_map.ipynb @@ -0,0 +1,944 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "771681dc", + "metadata": {}, + "source": [ + "# Implementations" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e48db800", + "metadata": {}, + "outputs": [], + "source": [ + "### Hash Table / Dictionary - Key-Value Mapping\n", + "\"\"\"\n", + "### Hash Table / Dictionary - Key-Value Mapping\n", + "\n", + "- Unordered collection of key-value pairs\n", + "- O(1) average case lookup, insertion, deletion\n", + "- Keys must be hashable (immutable): strings, numbers, tuples\n", + "- Values can be any type\n", + "- Python's dict is a hash table implementation\n", + "\"\"\"\n", + "# (Code)\n", + "\n", + "# Empty dictionary\n", + "d = {}\n", + "\n", + "# Dictionary with initial values\n", + "d = {\"name\": \"Alice\", \"age\": 30, \"city\": \"NYC\"}\n", + "\n", + "# Dictionary from pairs\n", + "d = dict([(\"x\", 1), (\"y\", 2)])\n", + "\n", + "# Dictionary comprehension\n", + "d = {i: i**2 for i in range(5)} # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}\n", + "\n", + "# Nested dictionary\n", + "person = {\n", + " \"name\": \"Bob\",\n", + " \"age\": 25,\n", + " \"address\": {\"street\": \"123 Main\", \"city\": \"LA\"}\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ba2eb704", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "HashTable({'apple': 5, 'banana': 3, 'cherry': 8})\n", + "5\n", + "True\n", + "3\n", + "2\n" + ] + } + ], + "source": [ + "### Complete Dictionary Implementation (Interview-Ready)\n", + "\n", + "\"\"\"\n", + "### Complete Dictionary Implementation (Interview-Ready)\n", + "\"\"\"\n", + "class HashTable:\n", + " def __init__(self):\n", + " self.table = {}\n", + " \n", + " def put(self, key, value):\n", + " \"\"\"Insert or update key-value pair - O(1) average\"\"\"\n", + " self.table[key] = value\n", + " \n", + " def get(self, key):\n", + " \"\"\"Retrieve value by key - O(1) average\"\"\"\n", + " if key not in self.table:\n", + " raise KeyError(f\"Key '{key}' not found\")\n", + " return self.table[key]\n", + " \n", + " def remove(self, key):\n", + " \"\"\"Delete key-value pair - O(1) average\"\"\"\n", + " if key not in self.table:\n", + " raise KeyError(f\"Key '{key}' not found\")\n", + " del self.table[key]\n", + " \n", + " def contains(self, key):\n", + " \"\"\"Check if key exists - O(1) average\"\"\"\n", + " return key in self.table\n", + " \n", + " def keys(self):\n", + " \"\"\"Get all keys - O(n)\"\"\"\n", + " return list(self.table.keys())\n", + " \n", + " def values(self):\n", + " \"\"\"Get all values - O(n)\"\"\"\n", + " return list(self.table.values())\n", + " \n", + " def items(self):\n", + " \"\"\"Get all key-value pairs - O(n)\"\"\"\n", + " return list(self.table.items())\n", + " \n", + " def size(self):\n", + " \"\"\"Return number of key-value pairs - O(1)\"\"\"\n", + " return len(self.table)\n", + " \n", + " def __repr__(self):\n", + " return f\"HashTable({self.table})\"\n", + "\n", + "# Usage\n", + "ht = HashTable()\n", + "ht.put(\"apple\", 5)\n", + "ht.put(\"banana\", 3)\n", + "ht.put(\"cherry\", 8)\n", + "print(ht) # HashTable({'apple': 5, 'banana': 3, 'cherry': 8})\n", + "print(ht.get(\"apple\")) # 5\n", + "print(ht.contains(\"banana\")) # True\n", + "print(ht.size()) # 3\n", + "ht.remove(\"banana\")\n", + "print(ht.size()) # 2" + ] + }, + { + "cell_type": "markdown", + "id": "c11836b1", + "metadata": {}, + "source": [ + "# Methods / Operations" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b066be66", + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "### .get(key, default=None)\n", + "\n", + "- Retrieve value by key - O(1) average\n", + "- Returns default if key not found (safe)\n", + "\"\"\"\n", + "# (Code)\n", + "d = {\"name\": \"Alice\", \"age\": 30}\n", + "print(d.get(\"name\")) # \"Alice\"\n", + "print(d.get(\"city\")) # None\n", + "print(d.get(\"city\", \"Unknown\")) # \"Unknown\"\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "f3118da7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n", + "{'x': 1, 'y': 2, 'z': 3}\n" + ] + } + ], + "source": [ + "\"\"\"\n", + "### dict[key] / dict[key] = value\n", + "\n", + "- Direct access and assignment - O(1) average\n", + "- Raises KeyError if key doesn't exist on access\n", + "\"\"\"\n", + "# (Code)\n", + "d = {\"x\": 1, \"y\": 2}\n", + "print(d[\"x\"]) # 1\n", + "d[\"z\"] = 3 # Add new key\n", + "print(d) # {'x': 1, 'y': 2, 'z': 3}\n", + "# d[\"w\"] # KeyError: 'w'\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "a234ed70", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dict_keys(['name', 'age', 'city'])\n", + "dict_values(['Alice', 30, 'NYC'])\n", + "dict_items([('name', 'Alice'), ('age', 30), ('city', 'NYC')])\n", + "['name', 'age', 'city']\n", + "['Alice', 30, 'NYC']\n" + ] + } + ], + "source": [ + "\"\"\"\n", + "### .keys() / .values() / .items()\n", + "\n", + "- Get all keys, values, or key-value pairs - O(n)\n", + "- Returns view objects (iterable)\n", + "\"\"\"\n", + "# (Code)\n", + "d = {\"name\": \"Alice\", \"age\": 30, \"city\": \"NYC\"}\n", + "print(d.keys()) # dict_keys(['name', 'age', 'city'])\n", + "print(d.values()) # dict_values(['Alice', 30, 'NYC'])\n", + "print(d.items()) # dict_items([('name', 'Alice'), ('age', 30), ('city', 'NYC')])\n", + "\n", + "# Convert to list if needed\n", + "print(list(d.keys())) # ['name', 'age', 'city']\n", + "print(list(d.values())) # ['Alice', 30, 'NYC']\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "3f0ebf6d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n", + "False\n", + "False\n", + "True\n" + ] + } + ], + "source": [ + "\"\"\"\n", + "### in / not in\n", + "\n", + "- Check if key exists - O(1) average\n", + "- Much faster than checking values\n", + "\"\"\"\n", + "# (Code)\n", + "d = {\"a\": 1, \"b\": 2, \"c\": 3}\n", + "print(\"a\" in d) # True\n", + "print(\"x\" in d) # False\n", + "print(\"a\" not in d) # False\n", + "\n", + "# Check if value exists (slower - O(n))\n", + "print(1 in d.values()) # True\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "0661de68", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "30\n", + "{'name': 'Alice'}\n", + "Unknown\n" + ] + } + ], + "source": [ + "\"\"\"\n", + "### .pop(key, default=None)\n", + "\n", + "- Remove and return value - O(1) average\n", + "- Returns default if key not found\n", + "\"\"\"\n", + "# (Code)\n", + "d = {\"name\": \"Alice\", \"age\": 30}\n", + "age = d.pop(\"age\")\n", + "print(age) # 30\n", + "print(d) # {'name': 'Alice'}\n", + "\n", + "# Pop with default (safe)\n", + "city = d.pop(\"city\", \"Unknown\")\n", + "print(city) # \"Unknown\"\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "165e179f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'a': 1, 'b': 20, 'c': 30}\n", + "{'a': 1, 'b': 20, 'c': 30, 'd': 40, 'e': 50}\n" + ] + } + ], + "source": [ + "\"\"\"\n", + "### .update(other)\n", + "\n", + "- Merge another dictionary or iterable - O(n)\n", + "- Overwrites existing keys\n", + "\"\"\"\n", + "# (Code)\n", + "d1 = {\"a\": 1, \"b\": 2}\n", + "d2 = {\"b\": 20, \"c\": 30}\n", + "d1.update(d2)\n", + "print(d1) # {'a': 1, 'b': 20, 'c': 30}\n", + "\n", + "# Update from iterable of pairs\n", + "d1.update([(\"d\", 40), (\"e\", 50)])\n", + "print(d1) # {'a': 1, 'b': 20, 'c': 30, 'd': 40, 'e': 50}\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "e554e206", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{}\n", + "0\n" + ] + } + ], + "source": [ + "\"\"\"\n", + "### .clear()\n", + "\n", + "- Remove all key-value pairs - O(n)\n", + "- Dictionary still exists but is empty\n", + "\"\"\"\n", + "# (Code)\n", + "d = {\"a\": 1, \"b\": 2}\n", + "d.clear()\n", + "print(d) # {}\n", + "print(len(d)) # 0\n" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "4f2b973e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'a': 1, 'b': [2, 3, 4]}\n", + "{'a': 99, 'b': [2, 3, 4]}\n" + ] + } + ], + "source": [ + "\"\"\"\n", + "### .copy()\n", + "\n", + "- Create shallow copy - O(n)\n", + "- Modifications don't affect original\n", + "\"\"\"\n", + "# (Code)\n", + "original = {\"a\": 1, \"b\": [2, 3]}\n", + "copy_dict = original.copy()\n", + "copy_dict[\"a\"] = 99\n", + "copy_dict[\"b\"].append(4)\n", + "\n", + "print(original) # {'a': 1, 'b': [2, 3, 4]} - nested list modified!\n", + "print(copy_dict) # {'a': 99, 'b': [2, 3, 4]}\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "7670777b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n", + "2\n", + "{'a': 1, 'b': 2}\n" + ] + } + ], + "source": [ + "\"\"\"\n", + "### .setdefault(key, default=None)\n", + "\n", + "- Get value or set default if not exists - O(1) average\n", + "- Useful for initializing values\n", + "\"\"\"\n", + "# (Code)\n", + "d = {\"a\": 1}\n", + "print(d.setdefault(\"a\", 99)) # 1 (key exists)\n", + "print(d.setdefault(\"b\", 2)) # 2 (set and return)\n", + "print(d) # {'a': 1, 'b': 2}\n" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "e54ad4d4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3\n" + ] + } + ], + "source": [ + "\"\"\"\n", + "### len()\n", + "\n", + "- Return number of key-value pairs - O(1)\n", + "\"\"\"\n", + "# (Code)\n", + "d = {\"a\": 1, \"b\": 2, \"c\": 3}\n", + "print(len(d)) # 3\n" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "2305bf57", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "name\n", + "age\n", + "city\n", + "Alice\n", + "30\n", + "NYC\n", + "name: Alice\n", + "age: 30\n", + "city: NYC\n" + ] + } + ], + "source": [ + "\"\"\"\n", + "### Iteration\n", + "\n", + "- Iterate over keys, values, or items - O(n)\n", + "\"\"\"\n", + "# (Code)\n", + "d = {\"name\": \"Alice\", \"age\": 30, \"city\": \"NYC\"}\n", + "\n", + "# Iterate keys (default)\n", + "for key in d:\n", + " print(key) # name, age, city\n", + "\n", + "# Iterate values\n", + "for value in d.values():\n", + " print(value) # Alice, 30, NYC\n", + "\n", + "# Iterate items\n", + "for key, value in d.items():\n", + " print(f\"{key}: {value}\") # name: Alice, age: 30, city: NYC" + ] + }, + { + "cell_type": "markdown", + "id": "b67b4723", + "metadata": {}, + "source": [ + "# Corner Cases TODO add examples for each case" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "8c005486", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Empty dict length: 0\n", + "None\n", + "N/A\n", + "Key not found\n" + ] + } + ], + "source": [ + "# Empty dictionary\n", + "\"\"\"\n", + "### Empty Dictionary\n", + "\n", + "- Operations on empty dict should handle gracefully\n", + "- Accessing missing key raises KeyError\n", + "- get() returns None or default safely\n", + "\"\"\"\n", + "empty_dict = {}\n", + "print(f\"Empty dict length: {len(empty_dict)}\") # 0\n", + "\n", + "# Safe access\n", + "print(empty_dict.get(\"x\")) # None\n", + "print(empty_dict.get(\"x\", \"N/A\")) # N/A\n", + "\n", + "# Try to access non-existent key\n", + "try:\n", + " print(empty_dict[\"x\"])\n", + "except KeyError:\n", + " print(\"Key not found\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "4d963fa2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Length: 1\n", + "Get: value\n", + "Contains: True\n" + ] + } + ], + "source": [ + "\"\"\"\n", + "### Single Key-Value Pair\n", + "\n", + "- Basic operations work with one item\n", + "- Easy to test functionality\n", + "\"\"\"\n", + "single_dict = {\"key\": \"value\"}\n", + "print(f\"Length: {len(single_dict)}\") # 1\n", + "print(f\"Get: {single_dict.get('key')}\") # value\n", + "print(f\"Contains: {'key' in single_dict}\") # True\n" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "9b6e3c05", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Large dict size: 100000\n", + "Access: 50000\n", + "Contains: True\n" + ] + } + ], + "source": [ + "\"\"\"\n", + "### Large Dictionary\n", + "\n", + "- Hash tables handle large datasets efficiently - O(1) lookup stays O(1)\n", + "- Memory usage grows linearly with size\n", + "- No performance degradation with number of entries\n", + "\"\"\"\n", + "large_dict = {}\n", + "for i in range(100000):\n", + " large_dict[f\"key_{i}\"] = i\n", + "\n", + "print(f\"Large dict size: {len(large_dict)}\") # 100000\n", + "print(f\"Access: {large_dict['key_50000']}\") # 50000 (O(1))\n", + "print(f\"Contains: {'key_99999' in large_dict}\") # True\n" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "fe79699b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'x': 3}\n", + "1\n" + ] + } + ], + "source": [ + "\"\"\"\n", + "### Duplicate Keys\n", + "\n", + "- Adding same key overwrites value\n", + "- Only one value per key\n", + "- Last assignment wins\n", + "\"\"\"\n", + "dup_dict = {}\n", + "dup_dict[\"x\"] = 1\n", + "dup_dict[\"x\"] = 2\n", + "dup_dict[\"x\"] = 3\n", + "print(dup_dict) # {'x': 3}\n", + "print(len(dup_dict)) # 1\n" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "48bf38aa", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'1': 'string key', 1: 'integer key'}\n", + "2\n" + ] + } + ], + "source": [ + "\"\"\"\n", + "### String Keys vs Integer Keys\n", + "\n", + "- Keys can be any hashable type\n", + "- String and integer keys treated separately\n", + "\"\"\"\n", + "mixed_dict = {}\n", + "mixed_dict[\"1\"] = \"string key\"\n", + "mixed_dict[1] = \"integer key\"\n", + "print(mixed_dict) # {'1': 'string key', 1: 'integer key'}\n", + "print(len(mixed_dict)) # 2\n" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "5b6bfa9e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "4\n", + "empty string\n", + "none value\n" + ] + } + ], + "source": [ + "\"\"\"\n", + "### Special Keys\n", + "\n", + "- Empty string, None, 0, False all valid keys\n", + "- Type matters (True and 1 are considered equal)\n", + "\"\"\"\n", + "special_dict = {\n", + " \"\": \"empty string\",\n", + " None: \"none value\",\n", + " 0: \"zero\",\n", + " False: \"false\",\n", + " (): \"empty tuple\"\n", + "}\n", + "print(len(special_dict)) # 4 (False and 0 conflict in some Python versions)\n", + "print(special_dict.get(\"\")) # empty string\n", + "print(special_dict.get(None)) # none value\n" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "60d8fd56", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'list_val': [1, 2, 3], 'dict_val': {'nested': 'dict'}, 'set_val': {1, 2, 3}}\n", + "Cannot use list as key\n", + "{(1, 2): 'value'}\n" + ] + } + ], + "source": [ + "\"\"\"\n", + "### Unhashable Values\n", + "\n", + "- Values can be anything (mutable or immutable)\n", + "- Keys must be hashable (tuples ok, lists not ok)\n", + "\"\"\"\n", + "valid_dict = {\n", + " \"list_val\": [1, 2, 3],\n", + " \"dict_val\": {\"nested\": \"dict\"},\n", + " \"set_val\": {1, 2, 3}\n", + "}\n", + "print(valid_dict)\n", + "\n", + "# This will fail - can't use list as key\n", + "try:\n", + " bad_dict = {[1, 2]: \"value\"}\n", + "except TypeError:\n", + " print(\"Cannot use list as key\")\n", + "\n", + "# This works - tuple is hashable\n", + "good_dict = {(1, 2): \"value\"}\n", + "print(good_dict) # {(1, 2): 'value'}\n" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "9daa24f1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Alice\n", + "NYC\n", + "10001\n" + ] + } + ], + "source": [ + "\"\"\"\n", + "### Nested Dictionary Access\n", + "\n", + "- Access nested values with chained keys\n", + "- Check existence before accessing\n", + "\"\"\"\n", + "nested = {\n", + " \"person\": {\n", + " \"name\": \"Alice\",\n", + " \"age\": 30,\n", + " \"address\": {\n", + " \"city\": \"NYC\",\n", + " \"zip\": \"10001\"\n", + " }\n", + " }\n", + "}\n", + "\n", + "# Safe access\n", + "print(nested[\"person\"][\"name\"]) # Alice\n", + "print(nested[\"person\"][\"address\"][\"city\"]) # NYC\n", + "\n", + "# Check nested key exists\n", + "if \"person\" in nested and \"address\" in nested[\"person\"]:\n", + " print(nested[\"person\"][\"address\"][\"zip\"])\n" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "287de6db", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['z', 'a', 'm']\n" + ] + } + ], + "source": [ + "\"\"\"\n", + "### Iteration Order\n", + "\n", + "- Python 3.7+: dictionaries maintain insertion order\n", + "- Order is guaranteed in modern Python\n", + "\"\"\"\n", + "ordered_dict = {}\n", + "ordered_dict[\"z\"] = 1\n", + "ordered_dict[\"a\"] = 2\n", + "ordered_dict[\"m\"] = 3\n", + "\n", + "print(list(ordered_dict.keys())) # ['z', 'a', 'm'] - insertion order\n" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "910845b6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n", + "True\n", + "False\n", + "None\n", + "None\n" + ] + } + ], + "source": [ + "\"\"\"\n", + "### Dictionary with None Values\n", + "\n", + "- None is a valid value\n", + "- Distinguish between missing key and None value\n", + "\"\"\"\n", + "none_dict = {\"a\": 1, \"b\": None, \"c\": 3}\n", + "print(\"a\" in none_dict) # True (value exists)\n", + "print(\"b\" in none_dict) # True (None is still a value)\n", + "print(\"d\" in none_dict) # False (key doesn't exist)\n", + "\n", + "print(none_dict.get(\"b\")) # None\n", + "print(none_dict.get(\"d\")) # None\n", + "# These look same but \"b\" exists, \"d\" doesn't!\n" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "7b065dea", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dict: {'key_0': 42, 'key_1': 42, 'key_2': 42, 'key_3': 42, 'key_4': 42}\n", + "Length: 5\n" + ] + } + ], + "source": [ + "\"\"\"\n", + "### All Identical Values\n", + "\n", + "- Multiple keys can have same value\n", + "- Important for frequency/count problems\n", + "\"\"\"\n", + "identical_dict = {}\n", + "for i in range(5):\n", + " identical_dict[f\"key_{i}\"] = 42\n", + "\n", + "print(f\"Dict: {identical_dict}\") # All values are 42\n", + "print(f\"Length: {len(identical_dict)}\") # 5 (5 different keys)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "95e99d61", + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "### Frequency/Count Pattern\n", + "\n", + "- Common use case: counting occurrences\n", + "\"\"\"\n", + "text = \"hello\"\n", + "freq = {}\n", + "for char in text:\n", + " freq[char] = freq.get(char, 0) + 1\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7f21c88", + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "### Dictionary as Function Argument\n", + "\n", + "- Mutable: changes inside function affect original\n", + "\"\"\"\n", + "def modify_dict(d):\n", + " d[\"new_key\"] = \"new_value\"\n", + "\n", + "original = {\"a\": 1}\n", + "modify_dict(original)\n", + "print(original) # {'a': 1, 'new_key': 'new_value'}" + ] + }, + { + "cell_type": "markdown", + "id": "065933a8", + "metadata": {}, + "source": [ + "# Techniques" + ] + }, + { + "cell_type": "markdown", + "id": "dd2976e8", + "metadata": {}, + "source": [ + "# Practice Projects" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Datastructures + Algorithms/7_heap.ipynb b/Datastructures + Algorithms/7_heap.ipynb new file mode 100644 index 0000000..ae61c29 --- /dev/null +++ b/Datastructures + Algorithms/7_heap.ipynb @@ -0,0 +1,176 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "7fed8284", + "metadata": {}, + "source": [ + "# Implementations" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "aea625e3", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[3, 5, 7]\n", + "3\n", + "[-7, -3, -5]\n", + "7\n", + "[1, 3, 7, 5, 9]\n" + ] + } + ], + "source": [ + "# Implementations\n", + "\n", + "\"\"\"\n", + "### Heap - Priority Queue Data Structure\n", + "\n", + "- Complete binary tree where each parent is smaller/larger than children\n", + "- Min-heap: parent ≤ children (smallest at root)\n", + "- Max-heap: parent ≥ children (largest at root)\n", + "- Python's heapq implements min-heap by default\n", + "- O(log n) insertion and deletion, O(1) peek minimum\n", + "\"\"\"\n", + "# (Code)\n", + "\n", + "import heapq\n", + "\n", + "# Min-heap (default)\n", + "min_heap = []\n", + "heapq.heappush(min_heap, 5)\n", + "heapq.heappush(min_heap, 3)\n", + "heapq.heappush(min_heap, 7)\n", + "print(min_heap) # [3, 5, 7]\n", + "print(heapq.heappop(min_heap)) # 3\n", + "\n", + "# Max-heap (use negative values)\n", + "max_heap = []\n", + "for val in [5, 3, 7]:\n", + " heapq.heappush(max_heap, -val)\n", + "print(max_heap) # [-7, -5, -3]\n", + "print(-heapq.heappop(max_heap)) # 7\n", + "\n", + "# Heapify existing list - O(n)\n", + "nums = [5, 3, 7, 1, 9]\n", + "heapq.heapify(nums)\n", + "print(nums) # [1, 3, 7, 5, 9]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a4ebbc57", + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "### Complete Heap Implementation (Interview-Ready)\n", + "\"\"\"\n", + "import heapq\n", + "\n", + "class MinHeap:\n", + " def __init__(self):\n", + " self.heap = []\n", + " \n", + " def push(self, val):\n", + " \"\"\"Insert value into heap - O(log n)\"\"\"\n", + " heapq.heappush(self.heap, val)\n", + " \n", + " def pop(self):\n", + " \"\"\"Remove and return minimum - O(log n)\"\"\"\n", + " if not self.heap:\n", + " raise IndexError(\"pop from empty heap\")\n", + " return heapq.heappop(self.heap)\n", + " \n", + " def peek(self):\n", + " \"\"\"View minimum without removing - O(1)\"\"\"\n", + " if not self.heap:\n", + " raise IndexError(\"peek from empty heap\")\n", + " return self.heap[0]\n", + " \n", + " def is_empty(self):\n", + " \"\"\"Check if heap is empty - O(1)\"\"\"\n", + " return len(self.heap) == 0\n", + " \n", + " def size(self):\n", + " \"\"\"Return number of elements - O(1)\"\"\"\n", + " return len(self.heap)\n", + " \n", + " def __repr__(self):\n", + " return f\"MinHeap({self.heap})\"\n", + "\n", + "# Usage\n", + "h = MinHeap()\n", + "h.push(5)\n", + "h.push(3)\n", + "h.push(7)\n", + "h.push(1)\n", + "print(h) # MinHeap([1, 3, 7, 5])\n", + "print(h.peek()) # 1\n", + "print(h.pop()) # 1\n", + "print(h.size()) # 3\n", + "print(h.is_empty()) # False" + ] + }, + { + "cell_type": "markdown", + "id": "f6e344ba", + "metadata": {}, + "source": [ + "# Methods / Operations" + ] + }, + { + "cell_type": "markdown", + "id": "34a1b83d", + "metadata": {}, + "source": [ + "# Corner Cases TODO add examples for each case" + ] + }, + { + "cell_type": "markdown", + "id": "6c30b81a", + "metadata": {}, + "source": [ + "# Techniques" + ] + }, + { + "cell_type": "markdown", + "id": "496404d9", + "metadata": {}, + "source": [ + "# Practice Projects" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Datastructures + Algorithms/1d_hash_table_dictionary_hash_map.ipynb b/Datastructures + Algorithms/8_graph.ipynb similarity index 86% rename from Datastructures + Algorithms/1d_hash_table_dictionary_hash_map.ipynb rename to Datastructures + Algorithms/8_graph.ipynb index 92c1dfa..57e8c82 100644 --- a/Datastructures + Algorithms/1d_hash_table_dictionary_hash_map.ipynb +++ b/Datastructures + Algorithms/8_graph.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "771681dc", + "id": "89fd6731", "metadata": {}, "source": [ "# Implementations" @@ -10,7 +10,7 @@ }, { "cell_type": "markdown", - "id": "c11836b1", + "id": "631fbd3a", "metadata": {}, "source": [ "# Methods / Operations" @@ -18,7 +18,7 @@ }, { "cell_type": "markdown", - "id": "b67b4723", + "id": "2282a177", "metadata": {}, "source": [ "# Corner Cases TODO add examples for each case" @@ -26,7 +26,7 @@ }, { "cell_type": "markdown", - "id": "065933a8", + "id": "9b8fb9a5", "metadata": {}, "source": [ "# Techniques" @@ -34,7 +34,7 @@ }, { "cell_type": "markdown", - "id": "dd2976e8", + "id": "aa7da57a", "metadata": {}, "source": [ "# Practice Projects" diff --git a/Datastructures + Algorithms/8_hash_set.ipynb b/Datastructures + Algorithms/8_hash_set.ipynb deleted file mode 100644 index e69de29..0000000 diff --git a/Datastructures + Algorithms/9_heap.ipynb b/Datastructures + Algorithms/9_heap.ipynb deleted file mode 100644 index e69de29..0000000 diff --git a/Python/4_functions.ipynb b/Python/4_functions.ipynb index 6f1a1ea..28791a2 100644 --- a/Python/4_functions.ipynb +++ b/Python/4_functions.ipynb @@ -101,20 +101,6 @@ "print(t.fahrenheit) # 32.0" ] }, - { - "cell_type": "markdown", - "id": "ce7225e6", - "metadata": {}, - "source": [ - "## Doctstrings\n" - ] - }, - { - "cell_type": "markdown", - "id": "8cab42d7", - "metadata": {}, - "source": [] - }, { "cell_type": "markdown", "id": "67ca0d08",