From 4560634eb6caa488fe3b9cef30db1d06331899c1 Mon Sep 17 00:00:00 2001 From: bhyun-kim Date: Mon, 10 Jun 2024 15:48:05 -0500 Subject: [PATCH 1/8] =?UTF-8?q?[bhyun-kim.py,=20=EA=B9=80=EB=B3=91?= =?UTF-8?q?=ED=98=84]=20Week=207=20Solutions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- remove-nth-node-from-end-of-list/bhyun-kim.py | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 remove-nth-node-from-end-of-list/bhyun-kim.py diff --git a/remove-nth-node-from-end-of-list/bhyun-kim.py b/remove-nth-node-from-end-of-list/bhyun-kim.py new file mode 100644 index 000000000..5393bb1e8 --- /dev/null +++ b/remove-nth-node-from-end-of-list/bhyun-kim.py @@ -0,0 +1,81 @@ +""" +19. Remove Nth Node From End of List +https://leetcode.com/problems/remove-nth-node-from-end-of-list/ +""" + + +from typing import Optional + +class ListNode: + def __init__(self, val=0, next=None): + self.val = val + self.next = next + +""" +Solution 1: +Two Pass Algorithm 1 + - First pass: Count the number of nodes and store the values in the list + - Second pass: Build the new list without the Nth node from the end + +Time complexity: O(N) + - Two passes are required +Space complexity: O(N) + - The list stores the values of the nodes + - The new nodes are created to build the new list +""" + +class Solution1: + def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]: + + vals = [] + + while head: + vals.append(head.val) + head = head.next + + dummy_node = ListNode() + tail = dummy_node + _n = len(vals) - n + vals = vals[:_n] + vals[_n+1:] + + for v in vals: + tail.next = ListNode(val=v) + tail = tail.next + + return dummy_node.next + + + +""" +Solution 2: +Reference: + [1] https://leetcode.com/problems/remove-nth-node-from-end-of-list/editorial/ + [2] https://www.algodale.com/problems/remove-nth-node-from-end-of-list/ +One Pass Algorithm + - Use two pointers to maintain a gap of n nodes between them + - When the first pointer reaches the end, the second pointer will be at the Nth node from the end + +Time complexity: O(N) + - Only one pass is required +Space complexity: O(1) + - No extra space is required +""" + +class Solution2: + def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]: + + dummy = ListNode() + dummy.next = head + first = dummy + second = dummy + + for _ in range(n+1): + first = first.next + + while first: + first = first.next + second = second.next + + second.next = second.next.next + + return dummy.next From bbc7ad36e4cefa3c3b9450c6090d00d831ca8593 Mon Sep 17 00:00:00 2001 From: bhyun-kim <94029750+bhyun-kim@users.noreply.github.com> Date: Wed, 12 Jun 2024 09:48:39 -0500 Subject: [PATCH 2/8] Change ambiguous variable naming --- remove-nth-node-from-end-of-list/bhyun-kim.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/remove-nth-node-from-end-of-list/bhyun-kim.py b/remove-nth-node-from-end-of-list/bhyun-kim.py index 5393bb1e8..32c379769 100644 --- a/remove-nth-node-from-end-of-list/bhyun-kim.py +++ b/remove-nth-node-from-end-of-list/bhyun-kim.py @@ -35,8 +35,8 @@ def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNod dummy_node = ListNode() tail = dummy_node - _n = len(vals) - n - vals = vals[:_n] + vals[_n+1:] + idx_to_remove = len(vals) - n + vals = vals[:idx_to_remove] + vals[idx_to_remove+1:] for v in vals: tail.next = ListNode(val=v) From 5161060241fc5a120093be4b46f056c21f6c0cac Mon Sep 17 00:00:00 2001 From: bhyun-kim Date: Wed, 12 Jun 2024 17:04:14 -0500 Subject: [PATCH 3/8] Add solution for 19. Remove Nth Node From End of List --- validate-binary-search-tree/bhyun-kim.py | 77 ++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 validate-binary-search-tree/bhyun-kim.py diff --git a/validate-binary-search-tree/bhyun-kim.py b/validate-binary-search-tree/bhyun-kim.py new file mode 100644 index 000000000..9cd5eea2b --- /dev/null +++ b/validate-binary-search-tree/bhyun-kim.py @@ -0,0 +1,77 @@ +""" +98. Validate Binary Search Tree +https://leetcode.com/problems/validate-binary-search-tree/ + + +""" +from typing import Optional + +class TreeNode: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + + +""" +Solution1: + Recursion + In the given problem, the subtree of a node has a range of values according to the previous nodes. + Thus, we can define a function that checks the validity of the subtree of a node with the range of values. + + - Define a function that checks the validity of the subtree of a node with the range of values + - Check the validity of the left subtree and the right subtree + - with the range of values that the left subtree and the right subtree should have + - If left < root < right, the subtree is valid + - If the left subtree and the right subtree are valid, call the function recursively for the left and right subtrees. + - before calling the function, update the range of values for the left and right subtrees + - If the left subtree and the right subtree are valid, return True + +Time complexity: O(N) + - The function is called recursively for each node + +Space complexity: O(N) + - The function stores the range of values for each node +""" + +class Solution1: + def isValidBST(self, root: Optional[TreeNode]) -> bool: + maximum = float('inf') + minimum = float('-inf') + return self.isValidSubTree( + root, maximum, minimum + ) + + def isValidSubTree(self, root, maximum, minimum): + if root.left is not None: + if root.val <= root.left.val: + return False + if not minimum < root.left.val < maximum: + return False + + if root.right is not None : + if root.val >= root.right.val: + return False + if not minimum < root.right.val < maximum: + return False + + if root.left is not None: + l_max = min(maximum, root.val) + is_left_valid = self.isValidSubTree( + root.left, l_max, minimum + ) + else: + is_left_valid = True + + if root.right is not None: + r_min = max(minimum, root.val) + is_right_valid = self.isValidSubTree( + root.right, maximum, r_min + ) + else: + is_right_valid = True + + if is_left_valid and is_right_valid: + return True + else: + return False \ No newline at end of file From 89fdd8eb7d2edfcf82a5c410ac672e71a70217c2 Mon Sep 17 00:00:00 2001 From: bhyun-kim Date: Wed, 12 Jun 2024 19:51:04 -0500 Subject: [PATCH 4/8] Add solution for 235. Lowest Common Ancestor of a Binary Search Tree --- .../bhyun-kim.py | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 lowest-common-ancestor-of-a-binary-search-tree/bhyun-kim.py diff --git a/lowest-common-ancestor-of-a-binary-search-tree/bhyun-kim.py b/lowest-common-ancestor-of-a-binary-search-tree/bhyun-kim.py new file mode 100644 index 000000000..733ea916a --- /dev/null +++ b/lowest-common-ancestor-of-a-binary-search-tree/bhyun-kim.py @@ -0,0 +1,36 @@ +""" +235. Lowest Common Ancestor of a Binary Search Tree +https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-search-tree/ +""" + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + +""" +Solution: + - If both p and q are greater than the root, the lowest common ancestor is in the right subtree. + - If both p and q are less than the root, the lowest common ancestor is in the left subtree. + - Otherwise, the root is the lowest common ancestor. + +Time complexity: O(N) + - The function is called recursively for each node +Space complexity: O(N) + - Maximum depth of the recursion is the height of the tree +""" + +class Solution: + def lowestCommonAncestor( + self, root: "TreeNode", p: "TreeNode", q: "TreeNode" + ) -> "TreeNode": + + if p.val > root.val and q.val > root.val: + return self.lowestCommonAncestor(root.right, p, q) + + elif p.val < root.val and q.val < root.val: + return self.lowestCommonAncestor(root.left, p, q) + else: + return root \ No newline at end of file From e41728dcaebac7dd65d719b0f85f4e1ec43ddc63 Mon Sep 17 00:00:00 2001 From: bhyun-kim Date: Thu, 13 Jun 2024 16:27:18 -0500 Subject: [PATCH 5/8] Solution for 102. Binary Tree Level Order Traversal --- .../bhyun-kim.py | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 binary-tree-level-order-traversal/bhyun-kim.py diff --git a/binary-tree-level-order-traversal/bhyun-kim.py b/binary-tree-level-order-traversal/bhyun-kim.py new file mode 100644 index 000000000..7d50ec310 --- /dev/null +++ b/binary-tree-level-order-traversal/bhyun-kim.py @@ -0,0 +1,70 @@ +""" +102. Binary Tree Level Order Traversal +https://leetcode.com/problems/binary-tree-level-order-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 + Breadth First Search (BFS) using Queue + + The problem is asking to return the node values at each level of the binary tree. + To solve this problem, we can use BFS algorithm with a queue. + We will traverse the tree level by level and store the node values at each level. + + 1. Initialize an empty list to store the output. + 2. Initialize an empty queue. + 3. Add the root node to the queue. + 4. While the queue is not empty, do the following: + - Get the size of the queue to know the number of nodes at the current level. + - Initialize an empty list to store the node values at the current level. + - Traverse the nodes at the current level and add the node values to the list. + - If the node has left or right child, add them to the queue. + - Decrease the level size by 1. + - Add the list of node values at the current level to the output. + 5. Return the output. + +Time complexity: O(N) + - We visit each node once + +Space complexity: O(N) + - The maximum number of nodes in the queue is the number of nodes at the last level + - The maximum number of nodes at the last level is N/2 + - The output list stores the node values at each level which is N + - Thus, the space complexity is O(N) + +""" +class Solution: + def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]: + if root is None: + return + output = [] + queue = [] + queue.append(root) + + while(len(queue) > 0): + level_size = len(queue) + level_output = [] + + while level_size > 0: + node = queue.pop(0) + level_output.append(node.val) + + if node.left is not None: + queue.append(node.left) + if node.right is not None: + queue.append(node.right) + + level_size -= 1 + + output.append(level_output) + + return output From a5b39a69827a804e31acab50ece6e7d5c4a86b7d Mon Sep 17 00:00:00 2001 From: bhyun-kim Date: Thu, 13 Jun 2024 17:18:40 -0500 Subject: [PATCH 6/8] Add solution for 143. Reorder List --- reorder-list/bhyun-kim.py | 73 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 reorder-list/bhyun-kim.py diff --git a/reorder-list/bhyun-kim.py b/reorder-list/bhyun-kim.py new file mode 100644 index 000000000..d1d3e79a5 --- /dev/null +++ b/reorder-list/bhyun-kim.py @@ -0,0 +1,73 @@ +""" +143. Reorder List +https://leetcode.com/problems/reorder-list/ +""" + +from typing import Optional + +class ListNode: + def __init__(self, val=0, next=None): + self.val = val + self.next = next + +""" +Solution: + To reorder the linked list, we can follow these steps: + First, find the middle of the linked list using the slow and fast pointers. + Reverse the second half of the linked list. + Merge the first half and the reversed second half. + + 1. Find the middle of the linked list using the slow and fast pointers + - Initialize the slow and fast pointers to the head of the linked list + - Move the slow pointer by one step and the fast pointer by two steps + until the fast pointer reaches the end of the linked list. + 2. Reverse the second half of the linked list + - Initialize the prev and curr pointers to None and the middle node, respectively + - Iterate through the second half of the linked list + - Store the next node of the current node + - Reverse the current node + - Move the prev and curr pointers to the next nodes + 3. Merge the first half and the reversed second half + - Initialize the first and second pointers to the head and the reversed second half, respectively + - Iterate through the linked list + - Store the next nodes of the first and second pointers + - Update the next pointers of the first and second pointers + - Move the first and second pointers to the next nodes + +Time complexity: O(N) + - We iterate through the linked list to find the middle node and reverse the second half + - The time complexity is O(N) + +Space complexity: O(1) + - We only use constant extra space for the pointers + - The space complexity is O(1) + +""" + + +class Solution: + def reorderList(self, head: Optional[ListNode]) -> None: + """ + Do not return anything, modify head in-place instead. + """ + if not head or not head.next: + return + + slow, fast = head, head + while fast and fast.next: + slow = slow.next + fast = fast.next.next + + prev, curr = None, slow + while curr: + next_temp = curr.next + curr.next = prev + prev = curr + curr = next_temp + + first, second = head, prev + while second.next: + tmp1, tmp2 = first.next, second.next + first.next = second + second.next = tmp1 + first, second = tmp1, tmp2 \ No newline at end of file From 4534007f96c0f7bf517068c771546dafb514fa8b Mon Sep 17 00:00:00 2001 From: bhyun-kim Date: Thu, 13 Jun 2024 17:19:24 -0500 Subject: [PATCH 7/8] Run formatter --- .../bhyun-kim.py | 10 +++-- .../bhyun-kim.py | 12 +++--- remove-nth-node-from-end-of-list/bhyun-kim.py | 15 ++++---- reorder-list/bhyun-kim.py | 6 ++- validate-binary-search-tree/bhyun-kim.py | 38 +++++++++---------- 5 files changed, 43 insertions(+), 38 deletions(-) diff --git a/binary-tree-level-order-traversal/bhyun-kim.py b/binary-tree-level-order-traversal/bhyun-kim.py index 7d50ec310..0e63cb52c 100644 --- a/binary-tree-level-order-traversal/bhyun-kim.py +++ b/binary-tree-level-order-traversal/bhyun-kim.py @@ -5,6 +5,7 @@ from typing import List, Optional + # Definition for a binary tree node. class TreeNode: def __init__(self, val=0, left=None, right=None): @@ -12,6 +13,7 @@ def __init__(self, val=0, left=None, right=None): self.left = left self.right = right + """ Solution Breadth First Search (BFS) using Queue @@ -42,6 +44,8 @@ def __init__(self, val=0, left=None, right=None): - Thus, the space complexity is O(N) """ + + class Solution: def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]: if root is None: @@ -50,10 +54,10 @@ def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]: queue = [] queue.append(root) - while(len(queue) > 0): + while len(queue) > 0: level_size = len(queue) level_output = [] - + while level_size > 0: node = queue.pop(0) level_output.append(node.val) @@ -61,7 +65,7 @@ def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]: if node.left is not None: queue.append(node.left) if node.right is not None: - queue.append(node.right) + queue.append(node.right) level_size -= 1 diff --git a/lowest-common-ancestor-of-a-binary-search-tree/bhyun-kim.py b/lowest-common-ancestor-of-a-binary-search-tree/bhyun-kim.py index 733ea916a..882a73db6 100644 --- a/lowest-common-ancestor-of-a-binary-search-tree/bhyun-kim.py +++ b/lowest-common-ancestor-of-a-binary-search-tree/bhyun-kim.py @@ -3,6 +3,7 @@ https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-search-tree/ """ + # Definition for a binary tree node. class TreeNode: def __init__(self, x): @@ -10,6 +11,7 @@ def __init__(self, x): self.left = None self.right = None + """ Solution: - If both p and q are greater than the root, the lowest common ancestor is in the right subtree. @@ -22,15 +24,15 @@ def __init__(self, x): - Maximum depth of the recursion is the height of the tree """ + class Solution: def lowestCommonAncestor( self, root: "TreeNode", p: "TreeNode", q: "TreeNode" ) -> "TreeNode": - - if p.val > root.val and q.val > root.val: + if p.val > root.val and q.val > root.val: return self.lowestCommonAncestor(root.right, p, q) - - elif p.val < root.val and q.val < root.val: + + elif p.val < root.val and q.val < root.val: return self.lowestCommonAncestor(root.left, p, q) else: - return root \ No newline at end of file + return root diff --git a/remove-nth-node-from-end-of-list/bhyun-kim.py b/remove-nth-node-from-end-of-list/bhyun-kim.py index 32c379769..5395888c3 100644 --- a/remove-nth-node-from-end-of-list/bhyun-kim.py +++ b/remove-nth-node-from-end-of-list/bhyun-kim.py @@ -6,11 +6,13 @@ from typing import Optional + class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next + """ Solution 1: Two Pass Algorithm 1 @@ -24,9 +26,9 @@ def __init__(self, val=0, next=None): - The new nodes are created to build the new list """ + class Solution1: def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]: - vals = [] while head: @@ -36,14 +38,13 @@ def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNod dummy_node = ListNode() tail = dummy_node idx_to_remove = len(vals) - n - vals = vals[:idx_to_remove] + vals[idx_to_remove+1:] + vals = vals[:idx_to_remove] + vals[idx_to_remove + 1 :] for v in vals: tail.next = ListNode(val=v) tail = tail.next - + return dummy_node.next - """ @@ -61,20 +62,20 @@ def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNod - No extra space is required """ + class Solution2: def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]: - dummy = ListNode() dummy.next = head first = dummy second = dummy - for _ in range(n+1): + for _ in range(n + 1): first = first.next while first: first = first.next - second = second.next + second = second.next second.next = second.next.next diff --git a/reorder-list/bhyun-kim.py b/reorder-list/bhyun-kim.py index d1d3e79a5..d65c634d5 100644 --- a/reorder-list/bhyun-kim.py +++ b/reorder-list/bhyun-kim.py @@ -5,11 +5,13 @@ from typing import Optional + class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next + """ Solution: To reorder the linked list, we can follow these steps: @@ -52,7 +54,7 @@ def reorderList(self, head: Optional[ListNode]) -> None: """ if not head or not head.next: return - + slow, fast = head, head while fast and fast.next: slow = slow.next @@ -70,4 +72,4 @@ def reorderList(self, head: Optional[ListNode]) -> None: tmp1, tmp2 = first.next, second.next first.next = second second.next = tmp1 - first, second = tmp1, tmp2 \ No newline at end of file + first, second = tmp1, tmp2 diff --git a/validate-binary-search-tree/bhyun-kim.py b/validate-binary-search-tree/bhyun-kim.py index 9cd5eea2b..e1dad8db7 100644 --- a/validate-binary-search-tree/bhyun-kim.py +++ b/validate-binary-search-tree/bhyun-kim.py @@ -6,13 +6,14 @@ """ from typing import Optional + class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right - + """ Solution1: Recursion @@ -34,44 +35,39 @@ def __init__(self, val=0, left=None, right=None): - The function stores the range of values for each node """ + class Solution1: def isValidBST(self, root: Optional[TreeNode]) -> bool: - maximum = float('inf') - minimum = float('-inf') - return self.isValidSubTree( - root, maximum, minimum - ) + maximum = float("inf") + minimum = float("-inf") + return self.isValidSubTree(root, maximum, minimum) def isValidSubTree(self, root, maximum, minimum): - if root.left is not None: - if root.val <= root.left.val: + if root.left is not None: + if root.val <= root.left.val: return False if not minimum < root.left.val < maximum: - return False + return False - if root.right is not None : - if root.val >= root.right.val: + if root.right is not None: + if root.val >= root.right.val: return False if not minimum < root.right.val < maximum: - return False + return False if root.left is not None: l_max = min(maximum, root.val) - is_left_valid = self.isValidSubTree( - root.left, l_max, minimum - ) + is_left_valid = self.isValidSubTree(root.left, l_max, minimum) else: is_left_valid = True if root.right is not None: r_min = max(minimum, root.val) - is_right_valid = self.isValidSubTree( - root.right, maximum, r_min - ) + is_right_valid = self.isValidSubTree(root.right, maximum, r_min) else: is_right_valid = True - + if is_left_valid and is_right_valid: return True - else: - return False \ No newline at end of file + else: + return False From 2f00241634d8482bded1ed8884a920cdec8dbcc6 Mon Sep 17 00:00:00 2001 From: bhyun-kim <94029750+bhyun-kim@users.noreply.github.com> Date: Sat, 15 Jun 2024 11:42:15 -0500 Subject: [PATCH 8/8] Update bhyun-kim.py --- validate-binary-search-tree/bhyun-kim.py | 36 +++++++----------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/validate-binary-search-tree/bhyun-kim.py b/validate-binary-search-tree/bhyun-kim.py index e1dad8db7..dddf7a59d 100644 --- a/validate-binary-search-tree/bhyun-kim.py +++ b/validate-binary-search-tree/bhyun-kim.py @@ -43,31 +43,15 @@ def isValidBST(self, root: Optional[TreeNode]) -> bool: return self.isValidSubTree(root, maximum, minimum) def isValidSubTree(self, root, maximum, minimum): - if root.left is not None: - if root.val <= root.left.val: - return False - if not minimum < root.left.val < maximum: - return False - if root.right is not None: - if root.val >= root.right.val: - return False - if not minimum < root.right.val < maximum: - return False - - if root.left is not None: - l_max = min(maximum, root.val) - is_left_valid = self.isValidSubTree(root.left, l_max, minimum) - else: - is_left_valid = True - - if root.right is not None: - r_min = max(minimum, root.val) - is_right_valid = self.isValidSubTree(root.right, maximum, r_min) - else: - is_right_valid = True - - if is_left_valid and is_right_valid: + if root is None: return True - else: - 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 + )