Skip to content

[bhyun-kim.py, 김병현] Week 7 Solutions #130

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jun 17, 2024
74 changes: 74 additions & 0 deletions binary-tree-level-order-traversal/bhyun-kim.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
"""
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
38 changes: 38 additions & 0 deletions lowest-common-ancestor-of-a-binary-search-tree/bhyun-kim.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"""
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
82 changes: 82 additions & 0 deletions remove-nth-node-from-end-of-list/bhyun-kim.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
"""
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
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)
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
75 changes: 75 additions & 0 deletions reorder-list/bhyun-kim.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
"""
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
57 changes: 57 additions & 0 deletions validate-binary-search-tree/bhyun-kim.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"""
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 is None:
return True

if not minimum < root.val < maximum:
return False

return self.isValidSubTree(
root.left, root.val, minimum
) and self.isValidSubTree(
root.right, maximum, root.val
)