Skip to content

Commit 653786f

Browse files
authored
Merge pull request #41 from miguelHx/miguel_2.5_sum_lists
Miguel 2.5 - Sum Lists [Python]
2 parents de25204 + 9d286f0 commit 653786f

File tree

1 file changed

+396
-0
lines changed

1 file changed

+396
-0
lines changed
Lines changed: 396 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,396 @@
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+
Suppose the digits are stored in forward order.
15+
Repeat the above problem.
16+
EXAMPLE
17+
Input: (6 -> 1 -> 7) + (2 -> 9 -> 5). That is, 617 + 295
18+
Result: 9 -> 1 -> 2. That is, 912
19+
"""
20+
import unittest
21+
22+
23+
class Node:
24+
def __init__(self, d: int):
25+
self.data = d
26+
self.next = None
27+
28+
def __repr__(self):
29+
return self.__str__()
30+
31+
def __str__(self):
32+
return '<Node Value: {}>'.format(self.data)
33+
34+
def __eq__(self, other: object):
35+
if not isinstance(other, Node):
36+
return NotImplemented
37+
return self.data == other.data
38+
39+
40+
class LinkedList:
41+
def __init__(self, *numbers: int):
42+
self.head = None
43+
self.tail = None
44+
self.size = 0
45+
for num in numbers:
46+
self.append_to_tail(num)
47+
48+
def append_to_tail(self, e) -> None:
49+
if isinstance(e, int):
50+
self._append_num(e)
51+
elif isinstance(e, Node):
52+
self._append_node(e)
53+
54+
def _append_num(self, d: int) -> None:
55+
if self.head is None:
56+
self.head = Node(d)
57+
self.tail = self.head
58+
else:
59+
end = Node(d)
60+
self.tail.next = end
61+
self.tail = end
62+
self.size += 1
63+
64+
def _append_node(self, n: Node) -> None:
65+
if self.head is None:
66+
self.head = n
67+
self.tail = self.head
68+
else:
69+
end = n
70+
self.tail.next = end
71+
self.tail = end
72+
self.size += 1
73+
74+
def append_to_head(self, d: int) -> None:
75+
new_head = Node(d)
76+
new_head.next = self.head
77+
if self.head is None:
78+
# if list is empty and we add
79+
# out first element, head AND tail
80+
# must point to same node
81+
self.tail = new_head
82+
self.head = new_head
83+
self.size += 1
84+
85+
def get_node_at(self, index: int) -> Node:
86+
if index < 0 or index >= self.size:
87+
raise IndexError('list index out of range')
88+
n = self.head
89+
for i in range(self.size):
90+
if i == index:
91+
return n
92+
n = n.next
93+
94+
def pop_head(self) -> Node:
95+
if self.head is None:
96+
raise IndexError('no head to pop')
97+
h = self.head
98+
h.next = None
99+
self.head = self.head.next
100+
self.size -= 1
101+
return h
102+
103+
def append(self, ll: 'LinkedList') -> None:
104+
self.tail.next = ll.head
105+
self.size += ll.size
106+
ll.head = None
107+
ll.size = 0
108+
109+
def reverse(self) -> None:
110+
"""
111+
Reverses this linked list in place
112+
:return:
113+
"""
114+
if self.head is None:
115+
return
116+
prev = self.head
117+
self.tail = prev
118+
curr = prev.next
119+
self.tail.next = None
120+
while curr is not None:
121+
old_next = curr.next
122+
curr.next = prev
123+
prev = curr
124+
curr = old_next
125+
self.head = prev
126+
127+
def __repr__(self):
128+
return self.__str__()
129+
130+
def __str__(self):
131+
if self.head is None:
132+
return '<empty>'
133+
ll = []
134+
n = self.head
135+
while n.next is not None:
136+
ll.append('{} -> '.format(n.data))
137+
n = n.next
138+
ll.append(str(n.data))
139+
return ''.join(ll)
140+
141+
def __eq__(self, other: object):
142+
if not isinstance(other, LinkedList):
143+
return NotImplemented
144+
a = self.head
145+
b = other.head
146+
while a is not None and b is not None:
147+
if a.data != b.data:
148+
return False
149+
# otherwise, advance both pointers
150+
a = a.next
151+
b = b.next
152+
return a is None and b is None
153+
154+
155+
def reverse_linked_list(ll: LinkedList) -> LinkedList:
156+
"""
157+
Takes in a linked list and returns a reversed copy
158+
:param ll: input linked list
159+
:return: reversed linked list
160+
"""
161+
output_ll = LinkedList()
162+
n = ll.head
163+
while n is not None:
164+
output_ll.append_to_head(n.data)
165+
n = n.next
166+
return output_ll
167+
168+
169+
def sum_lists_forward(ll1: LinkedList, ll2: LinkedList) -> LinkedList:
170+
"""
171+
Will add ll1 and ll2 where each element in the linked lists
172+
represent digits.
173+
Note: Each element in the input linked lists MUST be values
174+
between 0-9
175+
Digits are in forward order, for example:
176+
Input: (6 -> 1 -> 7) + (2 -> 9 -> 5). That is, 617 + 295
177+
Output: 9 -> 1 -> 2. That is, 912
178+
Runtime: O(n)
179+
Space Complexity: O(n) - output_ll
180+
:param ll1: first input linked list
181+
:param ll2: second input linked list
182+
:return: a linked list containing the result of the addition
183+
"""
184+
# reverse both lists
185+
reversed_ll1 = reverse_linked_list(ll1)
186+
reversed_ll2 = reverse_linked_list(ll2)
187+
# then, call sum_lists
188+
reversed_result = sum_lists(reversed_ll1, reversed_ll2)
189+
# reverse a final time
190+
reversed_result.reverse()
191+
return reversed_result
192+
193+
194+
def sum_lists(ll1: LinkedList, ll2: LinkedList) -> LinkedList:
195+
"""
196+
Will add ll1 and ll2 where each element in the linked lists
197+
represent digits.
198+
Note: Each element in the input linked lists MUST be values
199+
between 0-9
200+
Digits are in backward order, for example:
201+
Input: (7 -> 1 -> 6) + (5 -> 9 -> 2). That is, 617 + 295
202+
Result: 2 -> 1 -> 9. That is, 912
203+
Runtime: O(n)
204+
Space Complexity: O(n) - output_ll
205+
:param ll1: first input linked list
206+
:param ll2: second input linked list
207+
:return: a linked list containing the result of the addition
208+
"""
209+
shorter_ll, longer_ll = (ll1, ll2) if ll1.size < ll2.size else (ll2, ll1)
210+
n1 = shorter_ll.head
211+
n2 = longer_ll.head
212+
output_ll = LinkedList()
213+
carry = 0
214+
while n1 is not None:
215+
first = n1.data
216+
second = n2.data
217+
result = first + second + carry
218+
if result >= 10:
219+
carry = 1
220+
result -= 10 # extract digit in one's place
221+
else:
222+
carry = 0
223+
output_ll.append_to_tail(result)
224+
n1 = n1.next
225+
n2 = n2.next
226+
# loop through remaining longer list
227+
while n2 is not None:
228+
value = n2.data
229+
result = value + carry
230+
if result >= 10:
231+
carry = 1
232+
result -= 10
233+
else:
234+
carry = 0
235+
output_ll.append_to_tail(result)
236+
n2 = n2.next
237+
if carry > 0:
238+
output_ll.append_to_tail(carry)
239+
return output_ll
240+
241+
242+
class TestSumLists(unittest.TestCase):
243+
244+
def setUp(self):
245+
self.test_cases = [
246+
(
247+
LinkedList(7, 1, 6),
248+
LinkedList(5, 9, 2),
249+
LinkedList(2, 1, 9)
250+
),
251+
(
252+
LinkedList(1, 1, 1, 1),
253+
LinkedList(2, 2, 2, 2),
254+
LinkedList(3, 3, 3, 3)
255+
),
256+
(
257+
LinkedList(5),
258+
LinkedList(4),
259+
LinkedList(9)
260+
),
261+
(
262+
LinkedList(5),
263+
LinkedList(6),
264+
LinkedList(1, 1)
265+
),
266+
(
267+
LinkedList(7, 1, 9),
268+
LinkedList(5, 9, 2),
269+
LinkedList(2, 1, 2, 1)
270+
),
271+
(
272+
LinkedList(9, 9, 9, 9),
273+
LinkedList(9, 9, 9, 9),
274+
LinkedList(8, 9, 9, 9, 1)
275+
),
276+
(
277+
LinkedList(9, 9, 9, 9),
278+
LinkedList(9, 9),
279+
LinkedList(8, 9, 0, 0, 1)
280+
),
281+
(
282+
LinkedList(1, 2),
283+
LinkedList(1),
284+
LinkedList(2, 2)
285+
),
286+
(
287+
LinkedList(5),
288+
LinkedList(2, 1),
289+
LinkedList(7, 1)
290+
),
291+
(
292+
LinkedList(),
293+
LinkedList(1),
294+
LinkedList(1)
295+
),
296+
(
297+
LinkedList(),
298+
LinkedList(),
299+
LinkedList()
300+
)
301+
]
302+
303+
self.forward_test_cases = [
304+
(
305+
LinkedList(6, 1, 7),
306+
LinkedList(2, 9, 5),
307+
LinkedList(9, 1, 2)
308+
),
309+
(
310+
LinkedList(1, 1, 1, 1),
311+
LinkedList(2, 2, 2, 2),
312+
LinkedList(3, 3, 3, 3)
313+
),
314+
(
315+
LinkedList(3),
316+
LinkedList(2),
317+
LinkedList(5)
318+
),
319+
(
320+
LinkedList(5),
321+
LinkedList(6),
322+
LinkedList(1, 1)
323+
),
324+
(
325+
LinkedList(9, 9, 9, 9),
326+
LinkedList(9, 9, 9, 9),
327+
LinkedList(1, 9, 9, 9, 8)
328+
),
329+
(
330+
LinkedList(9, 9, 9, 9),
331+
LinkedList(9, 9),
332+
LinkedList(1, 0, 0, 9, 8)
333+
),
334+
(
335+
LinkedList(1, 2),
336+
LinkedList(4),
337+
LinkedList(1, 6)
338+
),
339+
(
340+
LinkedList(4, 2),
341+
LinkedList(5),
342+
LinkedList(4, 7)
343+
),
344+
(
345+
LinkedList(4, 2),
346+
LinkedList(9),
347+
LinkedList(5, 1)
348+
),
349+
(
350+
LinkedList(8, 2),
351+
LinkedList(4),
352+
LinkedList(8, 6)
353+
),
354+
(
355+
LinkedList(9, 1, 0, 2),
356+
LinkedList(1, 0),
357+
LinkedList(9, 1, 1, 2)
358+
),
359+
(
360+
LinkedList(9, 1, 9, 2),
361+
LinkedList(1, 0),
362+
LinkedList(9, 2, 0, 2)
363+
),
364+
(
365+
LinkedList(9, 9, 1, 9),
366+
LinkedList(2, 0),
367+
LinkedList(9, 9, 3, 9)
368+
),
369+
(
370+
LinkedList(),
371+
LinkedList(1),
372+
LinkedList(1)
373+
),
374+
(
375+
LinkedList(),
376+
LinkedList(),
377+
LinkedList()
378+
),
379+
(
380+
LinkedList(1, 2, 3),
381+
LinkedList(),
382+
LinkedList(1, 2, 3)
383+
)
384+
]
385+
386+
def test_sum_lists_forward_order(self):
387+
for ll1, ll2, expected in self.forward_test_cases:
388+
self.assertEqual(sum_lists_forward(ll1, ll2), expected, msg=(ll1, ll2))
389+
390+
def test_sum_lists(self):
391+
for ll1, ll2, expected in self.test_cases:
392+
self.assertEqual(sum_lists(ll1, ll2), expected, msg=(ll1, ll2))
393+
394+
395+
if __name__ == '__main__':
396+
unittest.main()

0 commit comments

Comments
 (0)