Skip to content

Commit

Permalink
new file: Additional Exercises/Easy 346. Moving Average from Data St…
Browse files Browse the repository at this point in the history
…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
open-minded13 committed Mar 17, 2024
1 parent 96cf247 commit d0911b8
Show file tree
Hide file tree
Showing 5 changed files with 324 additions and 5 deletions.
40 changes: 40 additions & 0 deletions Additional Exercises/Easy 346. Moving Average from Data Stream.py
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!")
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()
91 changes: 91 additions & 0 deletions Additional Exercises/Medium 71. Simplify Path.py
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.")
52 changes: 52 additions & 0 deletions Additional Exercises/Medium 841. Keys and Rooms.py
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!")
14 changes: 9 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,20 +114,24 @@ Current Progress: 72/75.

- [x] [Easy 408. Valid Word Abbreviation](https://leetcode.com/problems/valid-word-abbreviation/description/)
- [x] [Easy 680. Valid Palindrome II](https://leetcode.com/problems/valid-palindrome-ii/) | Algo: Two-Pointer Technique
- [x] [Easy 938. Range Sum of BST](https://leetcode.com/problems/range-sum-of-bst/)
- [x] [Medium 2. Add Two Numbers](https://leetcode.com/problems/add-two-numbers/description/) | Algo: Carry Management
- [x] [Medium 138. Copy List with Random Pointer](https://leetcode.com/problems/copy-list-with-random-pointer/description/) | DS: Linked List
- [x] [Medium 215. Kth Largest Element in an Array](https://leetcode.com/problems/buildings-with-an-ocean-view/description/) | Algo: Quickselect (Fastest in Average Cases), DS: Heap (Good in All Cases)
- [x] ⭐️ [Medium 227. Basic Calculator](https://leetcode.com/problems/basic-calculator-ii/description/) | DS: Stack
- [x] [Medium 314. Binary Tree Vertical Order Traversal](https://leetcode.com/problems/binary-tree-vertical-order-traversal/) | Algo: Breadth-First Search (BFS), DS: Queue
- [x] [Medium 339. Nested List Weight Sum](https://leetcode.com/problems/nested-list-weight-sum/description/)
- [x] [Medium 426. Convert Binary Search Tree to Sorted Doubly Linked List](https://leetcode.com/problems/convert-binary-search-tree-to-sorted-doubly-linked-list/description/)
- [x] ⭐️ [Medium 528. Random Pick with Weight.py](https://leetcode.com/problems/random-pick-with-weight/description/) | Algo: Binary Search; DS: Prefix Sum Array
- [x] [Medium 763. Partition Labels](https://leetcode.com/problems/partition-labels/) | Algo: Two-Pointer Technique, Dynamic Sliding Window, Greedy Method
- [x] [Medium 791. Custom Sort String](https://leetcode.com/problems/custom-sort-string/description/)
- [x] [Medium 841. Keys and Rooms](https://leetcode.com/problems/keys-and-rooms/description/)
- [x] [Medium 1249. Minimum Remove to Make Valid Parentheses](https://leetcode.com/problems/minimum-remove-to-make-valid-parentheses/description/) | DS: Stack
- [x] [Medium 1570. Dot Product of Two Sparse Vectors](https://leetcode.com/problems/dot-product-of-two-sparse-vectors/)
- [x] [Medium 1650. Lowest Common Ancestor of a Binary Tree III](https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree-iii/) | Algo: Depth-First Search
- [x] [Medium 1762. Buildings with an Ocean View](https://leetcode.com/problems/buildings-with-an-ocean-view/description/) | DS: Monotonic Stack

Sun:
- [x] [Medium 215. Kth Largest Element in an Array](https://leetcode.com/problems/buildings-with-an-ocean-view/description/) | Algo: Quickselect (Fastest in Average Cases), DS: Heap (Good in All Cases)
- [x] [Easy 938. Range Sum of BST](https://leetcode.com/problems/range-sum-of-bst/)
- [x] [Medium 791. Custom Sort String](https://leetcode.com/problems/custom-sort-string/description/)
- [x] [Medium 426. Convert Binary Search Tree to Sorted Doubly Linked List](https://leetcode.com/problems/convert-binary-search-tree-to-sorted-doubly-linked-list/description/)
## Today

- [x] [Easy 346. Moving Average from Data Stream](https://leetcode.com/problems/moving-average-from-data-stream/)
- [x] ⭐️ [Medium 71. Simplify Path](https://leetcode.com/problems/simplify-path/description/)

0 comments on commit d0911b8

Please sign in to comment.