Skip to content

Commit 5cdfc7c

Browse files
committed
Initial commit
0 parents  commit 5cdfc7c

File tree

349 files changed

+27349
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

349 files changed

+27349
-0
lines changed

README.md

Lines changed: 5623 additions & 0 deletions
Large diffs are not rendered by default.

Solutions/001.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"""
2+
Problem:
3+
4+
Given a list of numbers, return whether any two sums to k. For example, given
5+
[10, 15, 3, 7] and k of 17, return true since 10 + 7 is 17.
6+
7+
Bonus: Can you do this in one pass?
8+
"""
9+
10+
from typing import List
11+
12+
13+
def check_target_sum(arr: List[int], target: int) -> bool:
14+
# using hash list to store the previously seen values to get access to them in O(1)
15+
previous = set()
16+
for elem in arr:
17+
if (target - elem) in previous:
18+
return True
19+
previous.add(elem)
20+
return False
21+
22+
23+
if __name__ == "__main__":
24+
print(check_target_sum([], 17))
25+
print(check_target_sum([10, 15, 3, 7], 17))
26+
print(check_target_sum([10, 15, 3, 4], 17))
27+
28+
29+
"""
30+
SPECS:
31+
32+
TIME COMPLEXITY: O(n)
33+
SPACE COMPLEXITY: O(n)
34+
"""

Solutions/002.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
"""
2+
Problem:
3+
4+
Given an array of integers, return a new array such that each element at index i of the
5+
new array is the product of all the numbers in the original array except the one at i.
6+
7+
For example, if our input was [1, 2, 3, 4, 5], the expected output would be
8+
[120, 60, 40, 30, 24]. If our input was [3, 2, 1], the expected output would be
9+
[2, 3, 6].
10+
11+
Follow-up: what if you can't use division?
12+
"""
13+
14+
from typing import List
15+
16+
17+
def product_of_arr_except_ith_elem(arr: List[int]) -> int:
18+
length = len(arr)
19+
result = [1 for _ in range(length)]
20+
# multiplying all the elements on the left of the ith element in the 1st pass
21+
# and all the elements on the right of the ith element in the 2nd pass
22+
prod = 1
23+
for i in range(length):
24+
result[i] *= prod
25+
prod *= arr[i]
26+
prod = 1
27+
for i in range(length - 1, -1, -1):
28+
result[i] *= prod
29+
prod *= arr[i]
30+
return result
31+
32+
33+
if __name__ == "__main__":
34+
print(product_of_arr_except_ith_elem([1, 2, 3, 4, 5]))
35+
print(product_of_arr_except_ith_elem([3, 2, 1]))
36+
37+
38+
"""
39+
SPECS:
40+
41+
TIME COMPLEXITY: O(n)
42+
SPACE COMPLEXITY: O(n)
43+
"""

Solutions/003.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
"""
2+
Problem:
3+
4+
Write a program to serialize a tree into a string and deserialize a string into a tree.
5+
"""
6+
7+
from DataStructures.Queue import Queue
8+
from DataStructures.Tree import Node, BinaryTree
9+
10+
11+
def serialize_helper(node: Node) -> str:
12+
# helper function to serialize a binary tree (uses prefix traversal)
13+
# data is padded with single quotes (') and comma (,) is used as a delimiter
14+
if node.right is None and node.left is None:
15+
return f"'{node.val}','None','None'"
16+
elif node.left is not None and node.right is None:
17+
return f"'{node.val}',{serialize_helper(node.left)},'None'"
18+
elif node.left is None and node.right is not None:
19+
return f"'{node.val}','None',{serialize_helper(node.right)}"
20+
elif node.left is not None and node.right is not None:
21+
return (
22+
f"'{node.val}',"
23+
+ f"{serialize_helper(node.left)},"
24+
+ f"{serialize_helper(node.right)}"
25+
)
26+
27+
28+
def serialize(tree: BinaryTree) -> str:
29+
return serialize_helper(tree.root)
30+
31+
32+
def deserialize_helper(node: Node, queue: Queue) -> Node:
33+
# helper function to deserialize a string into a Binary Tree
34+
# data is a queue containing the data as a prefix notation can be easily decoded
35+
# using a queue
36+
left = queue.dequeue().strip("'")
37+
if left != "None":
38+
# if the left child exists, its added to the tree
39+
node.left = Node(left)
40+
node.left = deserialize_helper(node.left, queue)
41+
42+
right = queue.dequeue().strip("'")
43+
if right != "None":
44+
# if the right child exists, its added to the tree
45+
node.right = Node(right)
46+
node.right = deserialize_helper(node.right, queue)
47+
return node
48+
49+
50+
def deserialize(string: str) -> BinaryTree:
51+
# the string needs to have the same format as the binary tree serialization
52+
# eg: data is padded with single quotes (') and comma (,) is used as a delimiter
53+
data = string.split(",")
54+
queue = Queue()
55+
for node in data:
56+
queue.enqueue(node)
57+
tree = BinaryTree()
58+
tree.root = Node(queue.dequeue().strip("'"))
59+
deserialize_helper(tree.root, queue)
60+
return tree
61+
62+
63+
if __name__ == "__main__":
64+
tree = BinaryTree()
65+
66+
tree.root = Node("root")
67+
68+
tree.root.left = Node("left")
69+
tree.root.right = Node("right")
70+
71+
tree.root.left.left = Node("left.left")
72+
73+
print(serialize(tree))
74+
75+
generated_tree = deserialize(
76+
"'root','left','left.left','None','None','None','right','None','None'"
77+
)
78+
79+
print(serialize(generated_tree))
80+
81+
82+
"""
83+
SPECS:
84+
85+
SERIALIZE: (n = Number of Nodes)
86+
TIME COMPLEXITY: O(n)
87+
SPACE COMPLEXITY: O(n)
88+
89+
DESERIALIZE: (n = Number of Characters in the String)
90+
TIME COMPLEXITY: O(n)
91+
SPACE COMPLEXITY: O(n)
92+
"""

Solutions/004.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
"""
2+
Problem:
3+
4+
Given an array of integers, find the first missing positive integer in linear time and
5+
constant space. In other words, find the lowest positive integer that does not exist in
6+
the array. The array can contain duplicates and negative numbers as well.
7+
8+
For example, the input [3, 4, -1, 1] should give 2. The input [1, 2, 0] should give 3.
9+
10+
You can modify the input array in-place.
11+
"""
12+
13+
from typing import List
14+
15+
16+
def first_missing_positive_integer(arr: List[int]) -> int:
17+
# placing the positive elements (< length) in their proper position
18+
# proper position index = element - 1
19+
# if after the palcement is complete, index of the 1st element not in its proper
20+
# position is the answer
21+
length = len(arr)
22+
for i in range(length):
23+
correctPos = arr[i] - 1
24+
while 1 <= arr[i] <= length and arr[i] != arr[correctPos]:
25+
arr[i], arr[correctPos] = arr[correctPos], arr[i]
26+
correctPos = arr[i] - 1
27+
# finding the first missing positive integer
28+
for i in range(length):
29+
if i + 1 != arr[i]:
30+
return i + 1
31+
return length + 1
32+
33+
34+
if __name__ == "__main__":
35+
print(first_missing_positive_integer([3, 4, 2, 1]))
36+
print(first_missing_positive_integer([3, 4, -1, 1]))
37+
print(first_missing_positive_integer([1, 2, 5]))
38+
print(first_missing_positive_integer([-1, -2]))
39+
40+
41+
"""
42+
SPECS:
43+
44+
TIME COMPLEXITY: O(n)
45+
SPACE COMPLEXITY: O(1)
46+
47+
NOTE: Even though there is a nested loop it is a O(n) algorithm as the cap on the
48+
maximum iterations is 2 * n [Amortised analysis]
49+
"""

Solutions/005.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
"""
2+
Problem:
3+
4+
cons(a, b) constructs a pair, and car(pair) and cdr(pair) returns the first and last
5+
element of that pair. For example, car(cons(3, 4)) returns 3, and cdr(cons(3, 4))
6+
returns 4.
7+
8+
Given this implementation of cons:
9+
10+
def cons(a, b):
11+
def pair(f):
12+
return f(a, b)
13+
return pair
14+
"""
15+
16+
from typing import Callable
17+
18+
19+
# given implementation of cons:
20+
def cons(a, b):
21+
def pair(f):
22+
return f(a, b)
23+
24+
return pair
25+
26+
27+
# car implementation
28+
def car(f: Callable) -> int:
29+
z = lambda x, y: x
30+
return f(z)
31+
32+
33+
# cdr implementation
34+
def cdr(f: Callable) -> int:
35+
z = lambda x, y: y
36+
return f(z)
37+
38+
39+
if __name__ == "__main__":
40+
pair = cons(1, 3)
41+
42+
print(car(pair))
43+
print(cdr(pair))
44+
45+
46+
"""
47+
SPECS:
48+
49+
car:
50+
TIME COMPLEXITY: O(1)
51+
SPACE COMPLEXITY: O(1)
52+
53+
cdr:
54+
TIME COMPLEXITY: O(1)
55+
SPACE COMPLEXITY: O(1)
56+
"""

Solutions/006.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
"""
2+
Problem:
3+
4+
An XOR linked list is a more memory efficient doubly linked list. Instead of each node
5+
holding next and prev fields, it holds a field named both, which is an XOR of the next
6+
node and the previous node. Implement an XOR linked list; it has an add(element) which
7+
adds the element to the end, and a get(index) which returns the node at index.
8+
9+
If using a language that has no pointers (such as Python), you can assume you have
10+
access to get_pointer and dereference_pointer functions that converts between nodes
11+
and memory addresses.
12+
"""
13+
14+
15+
# Solution copied from:
16+
# https://github.com/r1cc4rdo/daily_coding_problem/blob/master/problems/06
17+
18+
19+
"""
20+
An XOR linked list is a more memory efficient doubly linked list.
21+
Instead of each node holding next and prev fields, it holds a field named both, which
22+
is a XOR of the next node and the previous node. Implement a XOR linked list; it has an
23+
add(element) which adds the element to the end, and a get(index) which returns the node
24+
at index.
25+
26+
NOTE: python does not have actual pointers (id() exists but it is not an actual pointer
27+
in all implementations). For this reason, we use a python list to simulate memory.
28+
Indexes are the addresses in memory. This has the unfortunate consequence that the
29+
travel logic needs to reside in the List class rather than the Node one.
30+
"""
31+
32+
from typing import Tuple
33+
34+
35+
class XORLinkedListNode:
36+
def __init__(self, val: int, prev: int, next: int) -> None:
37+
self.val = val
38+
self.both = prev ^ next
39+
40+
def next_node(self, prev_idx: int) -> int:
41+
return self.both ^ prev_idx
42+
43+
def prev_node(self, next_idx: int) -> int:
44+
return self.both ^ next_idx
45+
46+
47+
class XORLinkedList:
48+
def __init__(self) -> None:
49+
self.memory = [XORLinkedListNode(None, -1, -1)]
50+
51+
def head(self) -> Tuple[int, int, XORLinkedListNode]:
52+
# head node index, prev node index, head node
53+
return 0, -1, self.memory[0]
54+
55+
def add(self, val: int) -> None:
56+
current_node_index, previous_node_index, current_node = self.head()
57+
while True:
58+
# walk down the list until the end is found
59+
next_node_index = current_node.next_node(previous_node_index)
60+
if next_node_index == -1:
61+
# the end is reached
62+
break
63+
previous_node_index, current_node_index = (
64+
current_node_index,
65+
next_node_index,
66+
)
67+
current_node = self.memory[next_node_index]
68+
# allocation
69+
new_node_index = len(self.memory)
70+
current_node.both = previous_node_index ^ new_node_index
71+
self.memory.append(XORLinkedListNode(val, current_node_index, -1))
72+
73+
def get(self, index: int) -> int:
74+
current_index, previous_index, current_node = self.head()
75+
for _ in range(index + 1):
76+
previous_index, current_index = (
77+
current_index,
78+
current_node.next_node(previous_index),
79+
)
80+
current_node = self.memory[current_index]
81+
return current_node.val
82+
83+
84+
if __name__ == "__main__":
85+
xor_linked_list = XORLinkedList()
86+
87+
xor_linked_list.add(1)
88+
xor_linked_list.add(2)
89+
xor_linked_list.add(3)
90+
xor_linked_list.add(4)
91+
92+
print(xor_linked_list.get(0))
93+
print(xor_linked_list.get(1))
94+
print(xor_linked_list.get(2))
95+
print(xor_linked_list.get(3))

0 commit comments

Comments
 (0)