Skip to content

Commit 2e9e5f6

Browse files
committed
miguel 2.5 soln sum lists
1 parent 8ad3fff commit 2e9e5f6

File tree

1 file changed

+285
-0
lines changed

1 file changed

+285
-0
lines changed
Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
"""
2+
Python version 3.7.0
3+
2.5 - Sum Lists
4+
You have two numbers represented by a linked list,
5+
where each node contains a single digit.
6+
The digits are stored in reverse order, such that
7+
the 1's digit is at the head of the list. Write a
8+
function that adds the two numbers and returns the sum
9+
as a linked list.
10+
EXAMPLE
11+
Input: (7 -> 1 -> 6) + (5 -> 9 -> 2). That is, 617 + 295
12+
Result: 2 -> 1 -> 9. That is, 912
13+
FOLLOW UP
14+
"""
15+
import unittest
16+
17+
18+
class Node:
19+
def __init__(self, d: int):
20+
self.data = d
21+
self.next = None
22+
23+
def __repr__(self):
24+
return self.__str__()
25+
26+
def __str__(self):
27+
return '<Node Value: {}>'.format(self.data)
28+
29+
def __eq__(self, other: object):
30+
if not isinstance(other, Node):
31+
return NotImplemented
32+
return self.data == other.data
33+
34+
35+
class LinkedList:
36+
def __init__(self, *numbers: int):
37+
self.head = None
38+
self.tail = None
39+
self.size = 0
40+
for num in numbers:
41+
self.append_to_tail(num)
42+
43+
def append_to_tail(self, e) -> None:
44+
if isinstance(e, int):
45+
self._append_num(e)
46+
elif isinstance(e, Node):
47+
self._append_node(e)
48+
49+
def _append_num(self, d: int) -> None:
50+
if self.head is None:
51+
self.head = Node(d)
52+
self.tail = self.head
53+
else:
54+
end = Node(d)
55+
self.tail.next = end
56+
self.tail = end
57+
self.size += 1
58+
59+
def _append_node(self, n: Node) -> None:
60+
if self.head is None:
61+
self.head = n
62+
self.tail = self.head
63+
else:
64+
end = n
65+
self.tail.next = end
66+
self.tail = end
67+
self.size += 1
68+
69+
def append_to_head(self, d: int) -> None:
70+
new_head = Node(d)
71+
new_head.next = self.head
72+
if self.head is None:
73+
# if list is empty and we add
74+
# out first element, head AND tail
75+
# must point to same node
76+
self.tail = new_head
77+
self.head = new_head
78+
self.size += 1
79+
80+
def get_node_at(self, index: int) -> Node:
81+
if index < 0 or index >= self.size:
82+
raise IndexError('list index out of range')
83+
n = self.head
84+
for i in range(self.size):
85+
if i == index:
86+
return n
87+
n = n.next
88+
89+
def pop_head(self) -> Node:
90+
if self.head is None:
91+
raise IndexError('no head to pop')
92+
h = self.head
93+
h.next = None
94+
self.head = self.head.next
95+
self.size -= 1
96+
return h
97+
98+
def append(self, ll: 'LinkedList') -> None:
99+
self.tail.next = ll.head
100+
self.size += ll.size
101+
ll.head = None
102+
ll.size = 0
103+
104+
def __repr__(self):
105+
return self.__str__()
106+
107+
def __str__(self):
108+
if self.head is None:
109+
return '<empty>'
110+
ll = []
111+
n = self.head
112+
while n.next is not None:
113+
ll.append('{} -> '.format(n.data))
114+
n = n.next
115+
ll.append(str(n.data))
116+
return ''.join(ll)
117+
118+
def __eq__(self, other: object):
119+
if not isinstance(other, LinkedList):
120+
return NotImplemented
121+
a = self.head
122+
b = other.head
123+
while a is not None and b is not None:
124+
if a.data != b.data:
125+
return False
126+
# otherwise, advance both pointers
127+
a = a.next
128+
b = b.next
129+
return a is None and b is None
130+
131+
132+
def sum_lists_forward(ll1: LinkedList, ll2: LinkedList) -> LinkedList:
133+
"""
134+
Will add ll1 and ll2 where each element in the linked lists
135+
represent digits.
136+
Precondition: ll1 and ll2 must be the same length.
137+
Digits are in forward order, for example:
138+
Input: (6 -> 1 -> 7) + (2 -> 9 -> 5). That is, 617 + 295
139+
Output: 9 -> 1 -> 2. That is, 912
140+
Runtime: O(n)
141+
Space Complexity: O(n) - output_ll
142+
:param ll1: first input linked list
143+
:param ll2: second input linked list
144+
:return: a linked list containing the result of the addition
145+
"""
146+
assert(ll1.size == ll2.size)
147+
n1 = ll1.head
148+
n2 = ll2.head
149+
output_ll = LinkedList()
150+
prev_node = None
151+
while n1 is not None:
152+
first = n1.data
153+
second = n2.data
154+
result = first + second
155+
if result >= 10:
156+
carry = 1
157+
result -= 10 # extract digit in one's place
158+
if prev_node is None:
159+
# for when the first addition yields a carry
160+
# and the output linked list is empty
161+
output_ll.append_to_head(carry)
162+
else:
163+
# add carry to previous digit
164+
prev_node.data += carry
165+
output_ll.append_to_tail(result)
166+
n1 = n1.next
167+
n2 = n2.next
168+
prev_node = output_ll.tail
169+
170+
# if carry > 0:
171+
# output_ll.append_to_tail(carry)
172+
return output_ll
173+
174+
175+
def sum_lists(ll1: LinkedList, ll2: LinkedList) -> LinkedList:
176+
"""
177+
Will add ll1 and ll2 where each element in the linked lists
178+
represent digits.
179+
Precondition: ll1 and ll2 must be the same length.
180+
Digits are in backward order, for example:
181+
Input: (7 -> 1 -> 6) + (5 -> 9 -> 2). That is, 617 + 295
182+
Result: 2 -> 1 -> 9. That is, 912
183+
Runtime: O(n)
184+
Space Complexity: O(n) - output_ll
185+
:param ll1: first input linked list
186+
:param ll2: second input linked list
187+
:return: a linked list containing the result of the addition
188+
"""
189+
assert(ll1.size == ll2.size)
190+
n1 = ll1.head
191+
n2 = ll2.head
192+
output_ll = LinkedList()
193+
carry = 0
194+
while n1 is not None:
195+
first = n1.data
196+
second = n2.data
197+
result = first + second + carry
198+
if result >= 10:
199+
carry = 1
200+
result -= 10 # extract digit in one's place
201+
else:
202+
carry = 0
203+
output_ll.append_to_tail(result)
204+
n1 = n1.next
205+
n2 = n2.next
206+
if carry > 0:
207+
output_ll.append_to_tail(carry)
208+
return output_ll
209+
210+
211+
class TestSumLists(unittest.TestCase):
212+
213+
def setUp(self):
214+
self.test_cases = [
215+
(
216+
LinkedList(7, 1, 6),
217+
LinkedList(5, 9, 2),
218+
LinkedList(2, 1, 9)
219+
),
220+
(
221+
LinkedList(1, 1, 1, 1),
222+
LinkedList(2, 2, 2, 2),
223+
LinkedList(3, 3, 3, 3)
224+
),
225+
(
226+
LinkedList(5),
227+
LinkedList(4),
228+
LinkedList(9)
229+
),
230+
(
231+
LinkedList(5),
232+
LinkedList(6),
233+
LinkedList(1, 1)
234+
),
235+
(
236+
LinkedList(7, 1, 9),
237+
LinkedList(5, 9, 2),
238+
LinkedList(2, 1, 2, 1)
239+
),
240+
(
241+
LinkedList(9, 9, 9, 9),
242+
LinkedList(9, 9, 9, 9),
243+
LinkedList(8, 9, 9, 9, 1)
244+
)
245+
]
246+
247+
self.forward_test_cases = [
248+
(
249+
LinkedList(6, 1, 7),
250+
LinkedList(2, 9, 5),
251+
LinkedList(9, 1, 2)
252+
),
253+
(
254+
LinkedList(1, 1, 1, 1),
255+
LinkedList(2, 2, 2, 2),
256+
LinkedList(3, 3, 3, 3)
257+
),
258+
(
259+
LinkedList(3),
260+
LinkedList(2),
261+
LinkedList(5)
262+
),
263+
(
264+
LinkedList(5),
265+
LinkedList(6),
266+
LinkedList(1, 1)
267+
),
268+
(
269+
LinkedList(9, 9, 9, 9),
270+
LinkedList(9, 9, 9, 9),
271+
LinkedList(1, 9, 9, 9, 8)
272+
)
273+
]
274+
275+
def test_sum_lists_forward_order(self):
276+
for ll1, ll2, expected in self.forward_test_cases:
277+
self.assertEqual(sum_lists_forward(ll1, ll2), expected, msg=(ll1, ll2))
278+
279+
def test_sum_lists(self):
280+
for ll1, ll2, expected in self.test_cases:
281+
self.assertEqual(sum_lists(ll1, ll2), expected, msg=(ll1, ll2))
282+
283+
284+
if __name__ == '__main__':
285+
unittest.main()

0 commit comments

Comments
 (0)