Skip to content

Commit b9d57f1

Browse files
committed
initial commit with EPI test harness
1 parent d417ee6 commit b9d57f1

File tree

244 files changed

+411942
-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.

244 files changed

+411942
-0
lines changed

epi/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# EPI (Elements of Programming Interviews)
2+
3+
This directory contains my solutions to problems from __Elements of Programming Interviews in Python__. The implemented solutions are all mine, while the test framework and data classes (i.e. code in `data_types` and `test_framework`) was copied from the book's [Github repo](https://github.com/adnanaziz/EPIJudge). I made a few modifications to allow the current directory structure.
4+
5+
Run with `python3 <solution_file>.py`.

epi/data_types/__init__.py

Whitespace-only changes.

epi/data_types/binary_tree_node.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from test_framework.binary_tree_utils import (binary_tree_to_string,
2+
equal_binary_trees)
3+
4+
5+
class BinaryTreeNode:
6+
def __init__(self, data=None, left=None, right=None):
7+
self.data = data
8+
self.left = left
9+
self.right = right
10+
11+
def __eq__(self, other):
12+
return equal_binary_trees(self, other)
13+
14+
def __repr__(self):
15+
return str(binary_tree_to_string(self))
16+
17+
def __str__(self):
18+
return self.__repr__()
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from test_framework.binary_tree_utils import binary_tree_to_string
2+
3+
4+
class BinaryTreeNode:
5+
def __init__(self, data=None, left=None, right=None, parent=None):
6+
self.data = data
7+
self.left = left
8+
self.right = right
9+
self.parent = parent
10+
11+
def __repr__(self):
12+
return str(binary_tree_to_string(self))
13+
14+
def __str__(self):
15+
return self.__repr__()

epi/data_types/list_node.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
class ListNode:
2+
def __init__(self, data=0, next=None):
3+
self.data = data
4+
self.next = next
5+
6+
def __eq__(self, other):
7+
a, b = self, other
8+
while a and b:
9+
if a.data != b.data:
10+
return False
11+
a, b = a.next, b.next
12+
return a is None and b is None
13+
14+
def __repr__(self):
15+
node = self
16+
visited = set()
17+
first = True
18+
19+
result = ''
20+
21+
while node:
22+
if first:
23+
first = False
24+
else:
25+
result += ' -> '
26+
27+
if id(node) in visited:
28+
if node.next is not node:
29+
result += str(node.data)
30+
result += ' -> ... -> '
31+
32+
result += str(node.data)
33+
result += ' -> ...'
34+
break
35+
else:
36+
result += str(node.data)
37+
visited.add(id(node))
38+
node = node.next
39+
40+
return result
41+
42+
def __str__(self):
43+
return self.__repr__()
44+
45+
46+
def list_size(node):
47+
result = 0
48+
visited = set()
49+
50+
while node is not None and id(node) not in visited:
51+
result += 1
52+
visited.add(id(node))
53+
node = node.next
54+
55+
return result

epi/intersect_sorted_arrays.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from typing import List
2+
3+
from test_framework import generic_test
4+
5+
6+
def intersect_two_sorted_arrays(A: List[int], B: List[int]) -> List[int]:
7+
# TODO - you fill in here.
8+
return []
9+
10+
11+
if __name__ == '__main__':
12+
exit(
13+
generic_test.generic_test_main('intersect_sorted_arrays.py',
14+
'intersect_sorted_arrays.tsv',
15+
intersect_two_sorted_arrays))

epi/test_framework/__init__.py

Whitespace-only changes.
Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
import collections
2+
3+
from test_framework.test_failure import PropertyName, TestFailure
4+
5+
6+
class TreePath:
7+
def __init__(self, prev=None, to_left=False):
8+
self._prev = prev
9+
self._to_left = to_left
10+
11+
def with_left(self):
12+
return TreePath(self, True)
13+
14+
def with_right(self):
15+
return TreePath(self, False)
16+
17+
def __str__(self):
18+
result = []
19+
node = self
20+
21+
while node:
22+
result.append('->left' if node._to_left else '->right')
23+
node = node._prev
24+
25+
result.reverse()
26+
result[0] = 'root'
27+
28+
return ''.join(result)
29+
30+
31+
def generate_preorder(tree):
32+
result = []
33+
s = [tree]
34+
35+
while s:
36+
node = s.pop()
37+
if not node:
38+
continue
39+
40+
result.append(node.data)
41+
s.append(node.right)
42+
s.append(node.left)
43+
44+
return result
45+
46+
47+
def generate_inorder(tree):
48+
result = []
49+
s = [tree]
50+
initial = True
51+
52+
if not tree:
53+
return result
54+
55+
while s:
56+
node = s.pop()
57+
58+
if initial:
59+
initial = False
60+
else:
61+
result.append(node.data)
62+
node = node.right
63+
64+
while node:
65+
s.append(node)
66+
node = node.left
67+
68+
return result
69+
70+
71+
def generate_postorder(tree):
72+
result = []
73+
s = [tree]
74+
75+
while s:
76+
node = s.pop()
77+
if not node:
78+
continue
79+
80+
result.append(node.data)
81+
s.append(node.left)
82+
s.append(node.right)
83+
84+
result.reverse()
85+
86+
return result
87+
88+
89+
def find_node(tree, val):
90+
s = [tree]
91+
92+
while s:
93+
node = s.pop()
94+
95+
if not node:
96+
continue
97+
98+
if node.data == val:
99+
return node
100+
101+
s.append(node.left)
102+
s.append(node.right)
103+
104+
return None
105+
106+
107+
def must_find_node(tree, val):
108+
result = find_node(tree, val)
109+
if result is None:
110+
raise RuntimeError('{} was not found in the tree'.format(val))
111+
return result
112+
113+
114+
def equal_binary_trees(tree1, tree2):
115+
s = [(tree1, tree2)]
116+
117+
while s:
118+
node1, node2 = s.pop()
119+
120+
if (node1 is None) != (node2 is None):
121+
return False
122+
123+
if node1:
124+
if node1.data != node2.data:
125+
return False
126+
s.append((node1.left, node2.left))
127+
s.append((node1.right, node2.right))
128+
129+
return True
130+
131+
132+
def assert_equal_binary_trees(expected, result):
133+
s = [(expected, result, TreePath())]
134+
135+
while s:
136+
expected, result, path = s.pop()
137+
138+
expected_data = expected.data if expected is not None else None
139+
result_data = result.data if result is not None else None
140+
141+
if expected_data != result_data:
142+
raise TestFailure() \
143+
.with_property(PropertyName.RESULT, result) \
144+
.with_property(PropertyName.EXPECTED, expected) \
145+
.with_mismatch_info(path, expected_data, result_data)
146+
147+
if expected is not None and result is not None:
148+
s.append((expected.left, result.left, path.with_left()))
149+
s.append((expected.right, result.right, path.with_right()))
150+
151+
152+
def binary_tree_to_string(tree):
153+
result = ''
154+
nodes = collections.deque()
155+
visited = set()
156+
first = True
157+
null_nodes_pending = 0
158+
159+
result += '['
160+
nodes.append(tree)
161+
162+
while nodes:
163+
node = nodes.popleft()
164+
if id(node) in visited:
165+
raise RuntimeError('Detected a cycle in the tree')
166+
if node:
167+
if first:
168+
first = False
169+
else:
170+
result += ', '
171+
172+
while null_nodes_pending:
173+
result += 'null, '
174+
null_nodes_pending -= 1
175+
176+
result += '"{}"'.format(node.data)
177+
178+
visited.add(id(node))
179+
nodes.append(node.left)
180+
nodes.append(node.right)
181+
else:
182+
null_nodes_pending += 1
183+
184+
result += ']'
185+
return result
186+
187+
188+
def binary_tree_height(tree):
189+
s = [(tree, 1)]
190+
height = 0
191+
192+
while s:
193+
node, node_height = s.pop()
194+
if not node:
195+
continue
196+
197+
height = max(height, node_height)
198+
s.append((node.right, node_height + 1))
199+
s.append((node.left, node_height + 1))
200+
201+
return height
202+
203+
204+
def binary_tree_size(tree):
205+
s = [tree]
206+
size = 0
207+
208+
while s:
209+
node = s.pop()
210+
if not node:
211+
continue
212+
213+
size += 1
214+
s.append(node.right)
215+
s.append(node.left)
216+
217+
return size
218+
219+
220+
# Python framework specific functions
221+
222+
223+
def is_object_tree_type(tree):
224+
return tree and hasattr(tree, 'data') and \
225+
hasattr(tree, 'left') and hasattr(tree, 'right')
226+
227+
228+
def convert_binary_tree_to_binary_tree_with_parent(tree):
229+
s = [(tree, None)]
230+
231+
while s:
232+
node, parent = s.pop()
233+
if not node:
234+
continue
235+
236+
node.parent = parent
237+
s.append((node.right, node))
238+
s.append((node.left, node))
239+
240+
return tree
241+
242+
243+
def strip_parent_link(tree):
244+
s = [tree]
245+
246+
while s:
247+
node = s.pop()
248+
if node is None:
249+
continue
250+
251+
node.parent = None
252+
s.append(node.left)
253+
s.append(node.right)
254+
255+
256+
if __name__ == '__main__':
257+
from binary_tree_node import BinaryTreeNode as N
258+
259+
tree = N(1, N(2, N(4), N(5, N(8), None)),
260+
N(3, N(6, None, N(9)), N(7, None, N(10, N(11), N(12)))))
261+
262+
assert generate_inorder(tree) == [4, 2, 8, 5, 1, 6, 9, 3, 7, 11, 10, 12]
263+
assert generate_preorder(tree) == [1, 2, 4, 5, 8, 3, 6, 9, 7, 10, 11, 12]
264+
assert generate_postorder(tree) == [4, 8, 5, 2, 9, 6, 11, 12, 10, 7, 3, 1]
265+
266+
path = TreePath().with_left().with_left().with_right().with_left()
267+
assert str(path) == "root->left->left->right->left"

epi/test_framework/config.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"timeoutSeconds": 0,
3+
"numFailedTestsBeforeStop": 1
4+
}

0 commit comments

Comments
 (0)