-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
new file: Additional Exercises/Easy 346. Moving Average from Data St…
…ream.py new file: Additional Exercises/Medium 426. Convert Binary Search Tree to Sorted Doubly Linked List.py new file: Additional Exercises/Medium 71. Simplify Path.py new file: Additional Exercises/Medium 841. Keys and Rooms.py modified: README.md
- Loading branch information
1 parent
96cf247
commit d0911b8
Showing
5 changed files
with
324 additions
and
5 deletions.
There are no files selected for viewing
40 changes: 40 additions & 0 deletions
40
Additional Exercises/Easy 346. Moving Average from Data Stream.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
from collections import deque | ||
|
||
# Date of Last Practice: Mar 16, 2024 | ||
# | ||
# Time Complexity: O(1). We only have a constant amount of computation. | ||
# | ||
# Space Complexity: O(N), where N is size and size is the maximum number of elements in the window. | ||
# This is because it maintains a deque (double-ended queue) | ||
# that contains at most size elements, | ||
# where size is the maximum number of elements in the window. | ||
|
||
|
||
class MovingAverage: | ||
# Step 1 - Initialize the class with maximum size, window, and sum tracker | ||
def __init__(self, size: int): | ||
self.max_size = size | ||
self.window = deque([]) | ||
self.sum_of_window = 0 | ||
|
||
# Step 2 - Add a new value and calculate the moving average | ||
def next(self, val: int) -> float: | ||
# If the window is full, remove the oldest value | ||
if len(self.window) >= self.max_size: | ||
self.sum_of_window -= self.window.popleft() | ||
# Add the new value to the window and update the sum | ||
self.sum_of_window += val | ||
self.window.append(val) | ||
# Return the average | ||
return self.sum_of_window / len(self.window) | ||
|
||
|
||
# Step 3 - Test cases | ||
if __name__ == "__main__": | ||
movingAverage = MovingAverage(3) | ||
# Test the functionality with provided values | ||
assert movingAverage.next(1) == 1.0, "Test case 1 failed" | ||
assert movingAverage.next(10) == 5.5, "Test case 2 failed" | ||
assert movingAverage.next(3) == 4.666666666666667, "Test case 3 failed" | ||
assert movingAverage.next(5) == 6.0, "Test case 4 failed" | ||
print("All test cases passed!") |
132 changes: 132 additions & 0 deletions
132
Additional Exercises/Medium 426. Convert Binary Search Tree to Sorted Doubly Linked List.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
from typing import Optional | ||
|
||
# Date of Last Practice: Mar 16, 2024 | ||
# | ||
# Time Complexity: O(N), where N is the number of nodes in the tree. | ||
# | ||
# Space Complexity: O(N), where N is the number of nodes in the tree. | ||
# The space complexity consists of the space used by | ||
# the recursion call stack during the inorder traversal. | ||
# The height of a BST can be O(N) in the worst case | ||
# (completely unbalanced tree) and O(log N) in the best case | ||
# (completely balanced tree). | ||
|
||
|
||
class Node: | ||
def __init__(self, val=0, left=None, right=None): | ||
self.val = val | ||
self.left = left | ||
self.right = right | ||
|
||
|
||
class Solution: | ||
def treeToDoublyList(self, root: "Optional[Node]") -> "Optional[Node]": | ||
if not root: | ||
return None | ||
|
||
# Initialize the previous and head node references | ||
self.prev = None | ||
self.head = None | ||
|
||
# Step 1 - Perform inorder traversal to link nodes | ||
def _inorder_traversal(node): | ||
if not node: | ||
return | ||
|
||
_inorder_traversal(node.left) | ||
|
||
# For the first node, set it as the head | ||
if not self.prev: | ||
self.head = node | ||
else: | ||
# Link the current node with the previous one | ||
node.left = self.prev | ||
self.prev.right = node | ||
|
||
self.prev = node | ||
|
||
_inorder_traversal(node.right) | ||
|
||
_inorder_traversal(root) | ||
|
||
# Step 2 - Make the list circular | ||
self.head.left = self.prev | ||
self.prev.right = self.head | ||
|
||
return self.head | ||
|
||
|
||
class AnotherSolution: | ||
# This solution has a space complexity of O(N). | ||
def treeToDoublyList(self, root: "Optional[Node]") -> "Optional[Node]": | ||
sorted_node = [] | ||
|
||
def _inorder_traversal(node): | ||
if node is None: | ||
return | ||
_inorder_traversal(node.left) | ||
sorted_node.append(node) | ||
_inorder_traversal(node.right) | ||
|
||
_inorder_traversal(root) | ||
|
||
head = None | ||
pre_node = None | ||
for index, node in enumerate(sorted_node): | ||
if index == 0: | ||
head = node | ||
head.left = sorted_node[-1] | ||
head.right = ( | ||
sorted_node[index + 1] if index + 1 < len(sorted_node) else head | ||
) | ||
pre_node = head | ||
else: | ||
node.left = pre_node | ||
node.right = ( | ||
sorted_node[index + 1] if index + 1 < len(sorted_node) else head | ||
) | ||
pre_node = node | ||
|
||
return head | ||
|
||
|
||
# Test cases | ||
def test_solution(): | ||
# Helper function to create a binary search tree from a list of values | ||
def create_bst_from_list(index, values): | ||
if index >= len(values) or values[index] is None: | ||
return None | ||
root = Node(values[index]) | ||
root.left = create_bst_from_list(2 * index + 1, values) | ||
root.right = create_bst_from_list(2 * index + 2, values) | ||
return root | ||
|
||
# Helper function to validate the doubly linked list | ||
def validate_dll(head, expected_values): | ||
values = [] | ||
current = head | ||
for _ in range(len(expected_values)): | ||
values.append(current.val) | ||
current = current.right | ||
if current == head: | ||
break | ||
assert values == expected_values, f"Expected {expected_values}, got {values}" | ||
|
||
sol = Solution() | ||
|
||
# Test case 1 | ||
values1 = [4, 2, 5, 1, 3] | ||
bst1 = create_bst_from_list(0, values1) | ||
head1 = sol.treeToDoublyList(bst1) | ||
validate_dll(head1, [1, 2, 3, 4, 5]) | ||
|
||
# Test case 2 | ||
values2 = [2, 1, 3] | ||
bst2 = create_bst_from_list(0, values2) | ||
head2 = sol.treeToDoublyList(bst2) | ||
validate_dll(head2, [1, 2, 3]) | ||
|
||
print("All test cases passed!") | ||
|
||
|
||
test_solution() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
# Date of Last Practice: Mar 16, 2024 | ||
# | ||
# Time Complexity: O(N), where N is the length of the input path. | ||
# The time complexity of the given solution primarily depends on | ||
# the iteration over the path string once. | ||
# | ||
# Space Complexity: O(N), where N is the length of the input path. | ||
# In the worst-case scenario (where n is the number of characters in the path), | ||
# the folder_file_names list could potentially hold a fraction of the input | ||
# if the path contains many valid directory names. | ||
# The list size is proportional to the path's length in such scenarios. | ||
|
||
|
||
class Solution: | ||
def simplifyPath(self, path: str) -> str: | ||
# Step 1 - Split the path by '/' | ||
segments = path.split("/") | ||
|
||
# Step 2 - Initialize a stack to hold directory names | ||
stack = [] | ||
|
||
for segment in segments: | ||
if segment == "..": | ||
# Step 3 - Pop last directory if '..' and stack is not empty | ||
if stack: | ||
stack.pop() | ||
# If segment == "" (empty string), this will be considered to be False in Python. | ||
elif segment and segment != ".": | ||
# Step 3 - Push directory name into stack | ||
stack.append(segment) | ||
|
||
# Step 4 - Join the stack into a canonical path | ||
canonical_path = "/" + "/".join(stack) | ||
return canonical_path | ||
|
||
|
||
class BadSolution: | ||
# While this solution is effective, it is somewhat complex and not straightforward | ||
# due to the detailed handling of special cases (single and double dots, slashes) | ||
|
||
def simplifyPath(self, path: str) -> str: | ||
folder_file_names = [] | ||
dot_counter = 0 | ||
|
||
starting_index = None | ||
path += "/" | ||
for index, char in enumerate(path): | ||
if dot_counter == 1 and char == "/" and index - 2 == starting_index: | ||
dot_counter = 0 | ||
starting_index = None | ||
elif dot_counter == 2 and char == "/" and index - 3 == starting_index: | ||
if folder_file_names: | ||
folder_file_names.pop() | ||
dot_counter = 0 | ||
starting_index = None | ||
elif (dot_counter == 1 or dot_counter == 2) and char != ".": | ||
dot_counter = 0 | ||
|
||
if char == "/": | ||
if starting_index is None or index - 1 == starting_index: | ||
starting_index = index | ||
else: | ||
folder_file_names.append(path[starting_index + 1 : index]) | ||
starting_index = index | ||
dot_counter = 0 | ||
elif char == ".": | ||
dot_counter += 1 | ||
|
||
canonical_path = "/" + "/".join(folder_file_names) | ||
return canonical_path | ||
|
||
|
||
# Test cases | ||
solution = Solution() | ||
|
||
# Test case 1 | ||
assert solution.simplifyPath("/home/") == "/home" | ||
|
||
# Test case 2 | ||
assert solution.simplifyPath("/../") == "/" | ||
|
||
# Test case 3 | ||
assert solution.simplifyPath("/home//foo/") == "/home/foo" | ||
|
||
# Test case 4 - More complex path | ||
assert solution.simplifyPath("/a/./b/../../c/") == "/c" | ||
|
||
# Test case 5 - Path with multiple sequential slashes and dots | ||
assert solution.simplifyPath("/a//b////c/d//././/..") == "/a/b/c" | ||
|
||
print("All test cases passed successfully.") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
from collections import deque | ||
from typing import List | ||
|
||
# Date of Last Practice: Mar 16, 2024 | ||
# | ||
# Time Complexity: O(N+K), where N is the number of rooms, and K is the total number of keys. | ||
# The sum of all keys (rooms[i].length) across all rooms is bounded by 3000, | ||
# and each key leads to a room that will be visited at most once. | ||
# | ||
# Space Complexity: O(N+K), where N is the number of rooms, and K is the total number of keys. | ||
# The visited_room set will at most contain n elements if all rooms are visited. | ||
# The key_queue can at most contain all the keys found in the rooms at any point. | ||
|
||
|
||
class Solution: | ||
def canVisitAllRooms(self, rooms: List[List[int]]) -> bool: | ||
# Initialize a queue for BFS and a set to keep track of visited rooms | ||
key_queue = deque(rooms[0]) | ||
visited_room = {0} | ||
|
||
while key_queue: | ||
# Pop the first key from the queue | ||
room_to_open = key_queue.popleft() | ||
if room_to_open in visited_room: | ||
continue | ||
# Add all new keys found in the room to the queue | ||
for key in rooms[room_to_open]: | ||
if key not in visited_room: | ||
key_queue.append(key) | ||
# Mark the current room as visited | ||
visited_room.add(room_to_open) | ||
|
||
# If all rooms have been visited, return True | ||
return len(visited_room) == len(rooms) | ||
|
||
|
||
# Test cases | ||
sol = Solution() | ||
|
||
# Test case 1: Possible to visit all rooms | ||
rooms1 = [[1], [2], [3], []] | ||
assert sol.canVisitAllRooms(rooms1) == True | ||
|
||
# Test case 2: Impossible to visit room 2 | ||
rooms2 = [[1, 3], [3, 0, 1], [2], [0]] | ||
assert sol.canVisitAllRooms(rooms2) == False | ||
|
||
# Example of a more complex test case | ||
rooms3 = [[1, 2, 3], [4], [5], [6], [7], [8], [9], [10], [11], [], [], []] | ||
assert sol.canVisitAllRooms(rooms3) == True | ||
|
||
print("All tests passed!") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters