diff --git a/combination-sum/bhyun-kim.py b/combination-sum/bhyun-kim.py new file mode 100644 index 000000000..5303cd5b5 --- /dev/null +++ b/combination-sum/bhyun-kim.py @@ -0,0 +1,49 @@ +""" +39. Combination Sum +https://leetcode.com/problems/combination-sum/ + +Solution + To solve this problem, we can use backtracking. + The idea is to explore all possible combinations starting from each candidate. + + - We can sort the candidates to avoid duplicates. + - We can create a helper function that takes the remaining target, the current combination, and the start index. + - If the remaining target is 0, we have found a valid combination, so we add it to the result. + - If the remaining target is negative, we return. + - We iterate through the candidates starting from the start index. + - We add the current candidate to the combination and recursively call the helper function with the updated target and combination. + - After the recursive call, we remove the current candidate from the combination. + +Time complexity: O(2^n) + - In the worst case, we explore all possible combinations. + - For each candidate, we have two choices: include it or exclude it. + +Space complexity: O(n) + - The recursive call stack has a maximum depth of n. +""" + +from typing import List + + +class Solution: + def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: + def backtrack(remain, comb, start): + if remain == 0: + result.append(list(comb)) + return + elif remain < 0: + return + + for i in range(start, len(candidates)): + current_candidate = candidates[i] + if current_candidate > remain: + break + + comb.append(current_candidate) + backtrack(remain - current_candidate, comb, i) + comb.pop() + + candidates.sort() + result = [] + backtrack(target, [], 0) + return result diff --git a/construct-binary-tree-from-preorder-and-inorder-traversal/bhyun-kim.py b/construct-binary-tree-from-preorder-and-inorder-traversal/bhyun-kim.py new file mode 100644 index 000000000..83a993483 --- /dev/null +++ b/construct-binary-tree-from-preorder-and-inorder-traversal/bhyun-kim.py @@ -0,0 +1,63 @@ +""" +105. Construct Binary Tree from Preorder and Inorder Traversal +https://leetcode.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/ +""" + +from typing import List, Optional + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + + +""" +Solution + To solve this problem, we can use a recursive approach. + We can use a helper function that takes the left and right index of the inorder array. + The helper function will create a root node with the value of the current preorder index. + Then, it will recursively call itself with the left and right index of the left and right subtree. + + - Create a dictionary that maps the value of the inorder array to its index. + - Create a variable to keep track of the current preorder index. + - Create a helper function that takes the left and right index of the inorder array. + - In the helper function, if the left index is greater than the right index, return None. + - Create a root node with the value of the current preorder index. + - Increment the preorder index. + - Recursively call the helper function with the left and right index of the left and right subtree. + - Return the root node. + +Time Complexity : O(n) + - The helper function is called n times. + - The dictionary lookup is O(1). + +Space Complexity : O(n) + - The dictionary has n elements. + - The recursive call stack has n elements. +""" + + +class Solution: + def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]: + inorder_index_map = {val: idx for idx, val in enumerate(inorder)} + + self.preorder_index = 0 + + def array_to_tree(left, right): + if left > right: + return None + + root_value = preorder[self.preorder_index] + self.preorder_index += 1 + + root = TreeNode(root_value) + + root.left = array_to_tree(left, inorder_index_map[root_value] - 1) + root.right = array_to_tree(inorder_index_map[root_value] + 1, right) + + return root + + return array_to_tree(0, len(inorder) - 1) diff --git a/implement-trie-prefix-tree/bhyun-kim.py b/implement-trie-prefix-tree/bhyun-kim.py new file mode 100644 index 000000000..8624fc137 --- /dev/null +++ b/implement-trie-prefix-tree/bhyun-kim.py @@ -0,0 +1,46 @@ +""" +208. Implement Trie (Prefix Tree) +https://leetcode.com/problems/implement-trie-prefix-tree/ + +Solution: + - Initialize the Trie class with an empty list of words. + - Insert a word by appending it to the list of words. + - Search for a word by checking if it is in the list of words. + - Check if a prefix exists by iterating through the list of words and checking if any word starts with the prefix. + +Tiem complexity: + - Insert: O(1) + - Appending to a list is O(1) + - Search: O(n) + - Searching for an element in a list is O(n) + - StartsWith: O(n) + - Iterating through a list is O(n) + +Space complexity: O(n) + - The list of words may have all the words in the trie. +""" + + +class Trie: + def __init__(self): + self.words = [] + + def insert(self, word: str) -> None: + self.words.append(word) + + def search(self, word: str) -> bool: + return word in self.words + + def startsWith(self, prefix: str) -> bool: + for word in self.words: + if word.startswith(prefix): + return True + + return False + + +# Your Trie object will be instantiated and called as such: +# obj = Trie() +# obj.insert(word) +# param_2 = obj.search(word) +# param_3 = obj.startsWith(prefix) diff --git a/kth-smallest-element-in-a-bst/bhyun-kim.py b/kth-smallest-element-in-a-bst/bhyun-kim.py new file mode 100644 index 000000000..9cf26737d --- /dev/null +++ b/kth-smallest-element-in-a-bst/bhyun-kim.py @@ -0,0 +1,40 @@ +""" +230. Kth Smallest Element in a BST +https://leetcode.com/problems/kth-smallest-element-in-a-bst/ + +Solution: + To solve this problem, we can use an in-order traversal of the binary search tree. + We can create a helper function that performs an in-order traversal of the tree and returns a list of the elements in sorted order. + Then, we can return the k-th element from the list. + +Time complexity: O(n) + - The in-order traversal visits each node once. + - The list concatenation is O(n). + +Space complexity: O(n) + - The list of elements has n elements. +""" + + +from typing import Optional + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + + +class Solution: + def kthSmallest(self, root: Optional[TreeNode], k: int) -> int: + return self.inOrderSearch(root)[k - 1] + + def inOrderSearch(self, root): + output = [] + if root: + output += self.inOrderSearch(root.left) + output += [root.val] + output += self.inOrderSearch(root.right) + return output diff --git a/validate-binary-search-tree/bhyun-kim.py b/validate-binary-search-tree/bhyun-kim.py index dddf7a59d..b84c1a594 100644 --- a/validate-binary-search-tree/bhyun-kim.py +++ b/validate-binary-search-tree/bhyun-kim.py @@ -43,15 +43,12 @@ def isValidBST(self, root: Optional[TreeNode]) -> bool: return self.isValidSubTree(root, maximum, minimum) def isValidSubTree(self, root, maximum, minimum): - if root is None: return True - if not minimum < root.val < maximum: - return False - + if not minimum < root.val < maximum: + return False + return self.isValidSubTree( root.left, root.val, minimum - ) and self.isValidSubTree( - root.right, maximum, root.val - ) + ) and self.isValidSubTree(root.right, maximum, root.val) diff --git a/word-search/bhyun-kim.py b/word-search/bhyun-kim.py new file mode 100644 index 000000000..ab5208f2a --- /dev/null +++ b/word-search/bhyun-kim.py @@ -0,0 +1,90 @@ +""" +79. Word Search +https://leetcode.com/problems/word-search/ + +Solution: + To solve this problem, we think of the board as a graph. + We can use a depth-first search (DFS) to explore all possible paths starting from each cell. + We can create a helper function that takes the current cell and the index of the current character in the word. + + - We can count the frequency of each character in the board. + - We can check if all characters in the word exist in the board. + - If we cannot find all characters in the board, we return False. + + - We can create a helper function that takes the current coordinates and the index in the word. + - If the index is equal to the length of the word, + it means we have found all characters in the word, so we return True. + - If the coordinates are out of bounds or the current character does not match, + we return False. + - We mark the current cell as visited and explore all possible directions. + - If any direction returns True, we return True. + - We unmark the current cell and return False. + + - We can find all indices of the first character in the word. + - We can start the DFS from each index and return True if any DFS returns True. + - If no DFS returns True, we return False. + +Time complexity: O(m*n*4^l) + - m and n are the dimensions of the board. + - l is the length of the word. + - We explore 4 directions at each cell, and the maximum depth is the length of the word. + +Space complexity: O(l) + - The recursive call stack has a maximum depth of the length of the word. + +""" + + +from typing import List + + +class Solution: + def exist(self, board: List[List[str]], word: str) -> bool: + if not board or not word: + return False + + m, n = len(board), len(board[0]) + + # Step 1: Check if all characters in the word exist in the board + char_count = {} + for row in board: + for char in row: + if char in char_count: + char_count[char] += 1 + else: + char_count[char] = 1 + + for char in word: + if char not in char_count or char_count[char] == 0: + return False + char_count[char] -= 1 + + # Helper function to check if the word can be found starting from (i, j) + def dfs(i, j, word_index): + if word_index == len(word): + return True + + if i < 0 or i >= m or j < 0 or j >= n or board[i][j] != word[word_index]: + return False + + temp = board[i][j] + board[i][j] = "#" # mark as visited + + # Explore all possible directions + found = ( + dfs(i + 1, j, word_index + 1) + or dfs(i - 1, j, word_index + 1) + or dfs(i, j + 1, word_index + 1) + or dfs(i, j - 1, word_index + 1) + ) + + board[i][j] = temp # unmark + return found + + # Step 2: Find all indices of the first character in the word + for i in range(m): + for j in range(n): + if board[i][j] == word[0] and dfs(i, j, 0): + return True + + return False