-
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.
- Loading branch information
0 parents
commit 5cdfc7c
Showing
349 changed files
with
27,349 additions
and
0 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
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,34 @@ | ||
""" | ||
Problem: | ||
Given a list of numbers, return whether any two sums to k. For example, given | ||
[10, 15, 3, 7] and k of 17, return true since 10 + 7 is 17. | ||
Bonus: Can you do this in one pass? | ||
""" | ||
|
||
from typing import List | ||
|
||
|
||
def check_target_sum(arr: List[int], target: int) -> bool: | ||
# using hash list to store the previously seen values to get access to them in O(1) | ||
previous = set() | ||
for elem in arr: | ||
if (target - elem) in previous: | ||
return True | ||
previous.add(elem) | ||
return False | ||
|
||
|
||
if __name__ == "__main__": | ||
print(check_target_sum([], 17)) | ||
print(check_target_sum([10, 15, 3, 7], 17)) | ||
print(check_target_sum([10, 15, 3, 4], 17)) | ||
|
||
|
||
""" | ||
SPECS: | ||
TIME COMPLEXITY: O(n) | ||
SPACE COMPLEXITY: O(n) | ||
""" |
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,43 @@ | ||
""" | ||
Problem: | ||
Given an array of integers, return a new array such that each element at index i of the | ||
new array is the product of all the numbers in the original array except the one at i. | ||
For example, if our input was [1, 2, 3, 4, 5], the expected output would be | ||
[120, 60, 40, 30, 24]. If our input was [3, 2, 1], the expected output would be | ||
[2, 3, 6]. | ||
Follow-up: what if you can't use division? | ||
""" | ||
|
||
from typing import List | ||
|
||
|
||
def product_of_arr_except_ith_elem(arr: List[int]) -> int: | ||
length = len(arr) | ||
result = [1 for _ in range(length)] | ||
# multiplying all the elements on the left of the ith element in the 1st pass | ||
# and all the elements on the right of the ith element in the 2nd pass | ||
prod = 1 | ||
for i in range(length): | ||
result[i] *= prod | ||
prod *= arr[i] | ||
prod = 1 | ||
for i in range(length - 1, -1, -1): | ||
result[i] *= prod | ||
prod *= arr[i] | ||
return result | ||
|
||
|
||
if __name__ == "__main__": | ||
print(product_of_arr_except_ith_elem([1, 2, 3, 4, 5])) | ||
print(product_of_arr_except_ith_elem([3, 2, 1])) | ||
|
||
|
||
""" | ||
SPECS: | ||
TIME COMPLEXITY: O(n) | ||
SPACE COMPLEXITY: O(n) | ||
""" |
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,92 @@ | ||
""" | ||
Problem: | ||
Write a program to serialize a tree into a string and deserialize a string into a tree. | ||
""" | ||
|
||
from DataStructures.Queue import Queue | ||
from DataStructures.Tree import Node, BinaryTree | ||
|
||
|
||
def serialize_helper(node: Node) -> str: | ||
# helper function to serialize a binary tree (uses prefix traversal) | ||
# data is padded with single quotes (') and comma (,) is used as a delimiter | ||
if node.right is None and node.left is None: | ||
return f"'{node.val}','None','None'" | ||
elif node.left is not None and node.right is None: | ||
return f"'{node.val}',{serialize_helper(node.left)},'None'" | ||
elif node.left is None and node.right is not None: | ||
return f"'{node.val}','None',{serialize_helper(node.right)}" | ||
elif node.left is not None and node.right is not None: | ||
return ( | ||
f"'{node.val}'," | ||
+ f"{serialize_helper(node.left)}," | ||
+ f"{serialize_helper(node.right)}" | ||
) | ||
|
||
|
||
def serialize(tree: BinaryTree) -> str: | ||
return serialize_helper(tree.root) | ||
|
||
|
||
def deserialize_helper(node: Node, queue: Queue) -> Node: | ||
# helper function to deserialize a string into a Binary Tree | ||
# data is a queue containing the data as a prefix notation can be easily decoded | ||
# using a queue | ||
left = queue.dequeue().strip("'") | ||
if left != "None": | ||
# if the left child exists, its added to the tree | ||
node.left = Node(left) | ||
node.left = deserialize_helper(node.left, queue) | ||
|
||
right = queue.dequeue().strip("'") | ||
if right != "None": | ||
# if the right child exists, its added to the tree | ||
node.right = Node(right) | ||
node.right = deserialize_helper(node.right, queue) | ||
return node | ||
|
||
|
||
def deserialize(string: str) -> BinaryTree: | ||
# the string needs to have the same format as the binary tree serialization | ||
# eg: data is padded with single quotes (') and comma (,) is used as a delimiter | ||
data = string.split(",") | ||
queue = Queue() | ||
for node in data: | ||
queue.enqueue(node) | ||
tree = BinaryTree() | ||
tree.root = Node(queue.dequeue().strip("'")) | ||
deserialize_helper(tree.root, queue) | ||
return tree | ||
|
||
|
||
if __name__ == "__main__": | ||
tree = BinaryTree() | ||
|
||
tree.root = Node("root") | ||
|
||
tree.root.left = Node("left") | ||
tree.root.right = Node("right") | ||
|
||
tree.root.left.left = Node("left.left") | ||
|
||
print(serialize(tree)) | ||
|
||
generated_tree = deserialize( | ||
"'root','left','left.left','None','None','None','right','None','None'" | ||
) | ||
|
||
print(serialize(generated_tree)) | ||
|
||
|
||
""" | ||
SPECS: | ||
SERIALIZE: (n = Number of Nodes) | ||
TIME COMPLEXITY: O(n) | ||
SPACE COMPLEXITY: O(n) | ||
DESERIALIZE: (n = Number of Characters in the String) | ||
TIME COMPLEXITY: O(n) | ||
SPACE COMPLEXITY: O(n) | ||
""" |
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,49 @@ | ||
""" | ||
Problem: | ||
Given an array of integers, find the first missing positive integer in linear time and | ||
constant space. In other words, find the lowest positive integer that does not exist in | ||
the array. The array can contain duplicates and negative numbers as well. | ||
For example, the input [3, 4, -1, 1] should give 2. The input [1, 2, 0] should give 3. | ||
You can modify the input array in-place. | ||
""" | ||
|
||
from typing import List | ||
|
||
|
||
def first_missing_positive_integer(arr: List[int]) -> int: | ||
# placing the positive elements (< length) in their proper position | ||
# proper position index = element - 1 | ||
# if after the palcement is complete, index of the 1st element not in its proper | ||
# position is the answer | ||
length = len(arr) | ||
for i in range(length): | ||
correctPos = arr[i] - 1 | ||
while 1 <= arr[i] <= length and arr[i] != arr[correctPos]: | ||
arr[i], arr[correctPos] = arr[correctPos], arr[i] | ||
correctPos = arr[i] - 1 | ||
# finding the first missing positive integer | ||
for i in range(length): | ||
if i + 1 != arr[i]: | ||
return i + 1 | ||
return length + 1 | ||
|
||
|
||
if __name__ == "__main__": | ||
print(first_missing_positive_integer([3, 4, 2, 1])) | ||
print(first_missing_positive_integer([3, 4, -1, 1])) | ||
print(first_missing_positive_integer([1, 2, 5])) | ||
print(first_missing_positive_integer([-1, -2])) | ||
|
||
|
||
""" | ||
SPECS: | ||
TIME COMPLEXITY: O(n) | ||
SPACE COMPLEXITY: O(1) | ||
NOTE: Even though there is a nested loop it is a O(n) algorithm as the cap on the | ||
maximum iterations is 2 * n [Amortised analysis] | ||
""" |
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,56 @@ | ||
""" | ||
Problem: | ||
cons(a, b) constructs a pair, and car(pair) and cdr(pair) returns the first and last | ||
element of that pair. For example, car(cons(3, 4)) returns 3, and cdr(cons(3, 4)) | ||
returns 4. | ||
Given this implementation of cons: | ||
def cons(a, b): | ||
def pair(f): | ||
return f(a, b) | ||
return pair | ||
""" | ||
|
||
from typing import Callable | ||
|
||
|
||
# given implementation of cons: | ||
def cons(a, b): | ||
def pair(f): | ||
return f(a, b) | ||
|
||
return pair | ||
|
||
|
||
# car implementation | ||
def car(f: Callable) -> int: | ||
z = lambda x, y: x | ||
return f(z) | ||
|
||
|
||
# cdr implementation | ||
def cdr(f: Callable) -> int: | ||
z = lambda x, y: y | ||
return f(z) | ||
|
||
|
||
if __name__ == "__main__": | ||
pair = cons(1, 3) | ||
|
||
print(car(pair)) | ||
print(cdr(pair)) | ||
|
||
|
||
""" | ||
SPECS: | ||
car: | ||
TIME COMPLEXITY: O(1) | ||
SPACE COMPLEXITY: O(1) | ||
cdr: | ||
TIME COMPLEXITY: O(1) | ||
SPACE COMPLEXITY: O(1) | ||
""" |
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,95 @@ | ||
""" | ||
Problem: | ||
An XOR linked list is a more memory efficient doubly linked list. Instead of each node | ||
holding next and prev fields, it holds a field named both, which is an XOR of the next | ||
node and the previous node. Implement an XOR linked list; it has an add(element) which | ||
adds the element to the end, and a get(index) which returns the node at index. | ||
If using a language that has no pointers (such as Python), you can assume you have | ||
access to get_pointer and dereference_pointer functions that converts between nodes | ||
and memory addresses. | ||
""" | ||
|
||
|
||
# Solution copied from: | ||
# https://github.com/r1cc4rdo/daily_coding_problem/blob/master/problems/06 | ||
|
||
|
||
""" | ||
An XOR linked list is a more memory efficient doubly linked list. | ||
Instead of each node holding next and prev fields, it holds a field named both, which | ||
is a XOR of the next node and the previous node. Implement a XOR linked list; it has an | ||
add(element) which adds the element to the end, and a get(index) which returns the node | ||
at index. | ||
NOTE: python does not have actual pointers (id() exists but it is not an actual pointer | ||
in all implementations). For this reason, we use a python list to simulate memory. | ||
Indexes are the addresses in memory. This has the unfortunate consequence that the | ||
travel logic needs to reside in the List class rather than the Node one. | ||
""" | ||
|
||
from typing import Tuple | ||
|
||
|
||
class XORLinkedListNode: | ||
def __init__(self, val: int, prev: int, next: int) -> None: | ||
self.val = val | ||
self.both = prev ^ next | ||
|
||
def next_node(self, prev_idx: int) -> int: | ||
return self.both ^ prev_idx | ||
|
||
def prev_node(self, next_idx: int) -> int: | ||
return self.both ^ next_idx | ||
|
||
|
||
class XORLinkedList: | ||
def __init__(self) -> None: | ||
self.memory = [XORLinkedListNode(None, -1, -1)] | ||
|
||
def head(self) -> Tuple[int, int, XORLinkedListNode]: | ||
# head node index, prev node index, head node | ||
return 0, -1, self.memory[0] | ||
|
||
def add(self, val: int) -> None: | ||
current_node_index, previous_node_index, current_node = self.head() | ||
while True: | ||
# walk down the list until the end is found | ||
next_node_index = current_node.next_node(previous_node_index) | ||
if next_node_index == -1: | ||
# the end is reached | ||
break | ||
previous_node_index, current_node_index = ( | ||
current_node_index, | ||
next_node_index, | ||
) | ||
current_node = self.memory[next_node_index] | ||
# allocation | ||
new_node_index = len(self.memory) | ||
current_node.both = previous_node_index ^ new_node_index | ||
self.memory.append(XORLinkedListNode(val, current_node_index, -1)) | ||
|
||
def get(self, index: int) -> int: | ||
current_index, previous_index, current_node = self.head() | ||
for _ in range(index + 1): | ||
previous_index, current_index = ( | ||
current_index, | ||
current_node.next_node(previous_index), | ||
) | ||
current_node = self.memory[current_index] | ||
return current_node.val | ||
|
||
|
||
if __name__ == "__main__": | ||
xor_linked_list = XORLinkedList() | ||
|
||
xor_linked_list.add(1) | ||
xor_linked_list.add(2) | ||
xor_linked_list.add(3) | ||
xor_linked_list.add(4) | ||
|
||
print(xor_linked_list.get(0)) | ||
print(xor_linked_list.get(1)) | ||
print(xor_linked_list.get(2)) | ||
print(xor_linked_list.get(3)) |
Oops, something went wrong.