Skip to content

Commit 7be8347

Browse files
committed
miguel soln to 2.6 palindrome
1 parent 8ad3fff commit 7be8347

File tree

1 file changed

+218
-0
lines changed

1 file changed

+218
-0
lines changed
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
"""
2+
Python version 3.7.0
3+
2.6 - Palindrome
4+
Implement a function to check if a linked list
5+
is a palindrome.
6+
7+
"""
8+
import unittest
9+
10+
11+
class Node:
12+
def __init__(self, d: int):
13+
self.data = d
14+
self.next = None
15+
16+
def __repr__(self):
17+
return self.__str__()
18+
19+
def __str__(self):
20+
return '<Node Value: {}>'.format(self.data)
21+
22+
def __eq__(self, other: object):
23+
if not isinstance(other, Node):
24+
return NotImplemented
25+
return self.data == other.data
26+
27+
28+
class LinkedList:
29+
def __init__(self, *numbers: int):
30+
self.head = None
31+
self.tail = None
32+
self.size = 0
33+
for num in numbers:
34+
self.append_to_tail(num)
35+
36+
def append_to_tail(self, e) -> None:
37+
if isinstance(e, int):
38+
self._append_num(e)
39+
elif isinstance(e, Node):
40+
self._append_node(e)
41+
42+
def _append_num(self, d: int) -> None:
43+
if self.head is None:
44+
self.head = Node(d)
45+
self.tail = self.head
46+
else:
47+
end = Node(d)
48+
self.tail.next = end
49+
self.tail = end
50+
self.size += 1
51+
52+
def _append_node(self, n: Node) -> None:
53+
if self.head is None:
54+
self.head = n
55+
self.tail = self.head
56+
else:
57+
end = n
58+
self.tail.next = end
59+
self.tail = end
60+
self.size += 1
61+
62+
def append_to_head(self, d: int) -> None:
63+
new_head = Node(d)
64+
new_head.next = self.head
65+
if self.head is None:
66+
# if list is empty and we add
67+
# out first element, head AND tail
68+
# must point to same node
69+
self.tail = new_head
70+
self.head = new_head
71+
self.size += 1
72+
73+
def get_node_at(self, index: int) -> Node:
74+
if index < 0 or index >= self.size:
75+
raise IndexError('list index out of range')
76+
n = self.head
77+
for i in range(self.size):
78+
if i == index:
79+
return n
80+
n = n.next
81+
82+
def pop_head(self) -> Node:
83+
if self.head is None:
84+
raise IndexError('no head to pop')
85+
h = self.head
86+
h.next = None
87+
self.head = self.head.next
88+
self.size -= 1
89+
return h
90+
91+
def append(self, ll: 'LinkedList') -> None:
92+
self.tail.next = ll.head
93+
self.size += ll.size
94+
ll.head = None
95+
ll.size = 0
96+
97+
def reverse(self) -> None:
98+
"""
99+
Reverses this linked list in place
100+
:return:
101+
"""
102+
if self.head is None:
103+
return
104+
prev = self.head
105+
self.tail = prev
106+
curr = prev.next
107+
self.tail.next = None
108+
while curr is not None:
109+
old_next = curr.next
110+
curr.next = prev
111+
prev = curr
112+
curr = old_next
113+
self.head = prev
114+
115+
def __repr__(self):
116+
return self.__str__()
117+
118+
def __str__(self):
119+
if self.head is None:
120+
return '<empty>'
121+
ll = []
122+
n = self.head
123+
while n.next is not None:
124+
ll.append('{} -> '.format(n.data))
125+
n = n.next
126+
ll.append(str(n.data))
127+
return ''.join(ll)
128+
129+
def __eq__(self, other: object):
130+
if not isinstance(other, LinkedList):
131+
return NotImplemented
132+
a = self.head
133+
b = other.head
134+
while a is not None and b is not None:
135+
if a.data != b.data:
136+
return False
137+
# otherwise, advance both pointers
138+
a = a.next
139+
b = b.next
140+
return a is None and b is None
141+
142+
143+
def reverse_linked_list(ll: LinkedList) -> LinkedList:
144+
"""
145+
Takes in a linked list and returns a reversed copy
146+
:param ll: input linked list
147+
:return: reversed linked list
148+
"""
149+
output_ll = LinkedList()
150+
n = ll.head
151+
while n is not None:
152+
output_ll.append_to_head(n.data)
153+
n = n.next
154+
return output_ll
155+
156+
157+
def palindrome(ll: LinkedList) -> bool:
158+
"""
159+
Given a linked list, this function will check if the
160+
linked list is a palindrome.
161+
A palindrome is a word or phrase that is the same
162+
forwards and backwards.
163+
Since I will use integer nodes for the linked list,
164+
we will be checking if the sequence of numbers
165+
in a linked list is a palindrome.
166+
Runtime: O(n)
167+
Space Complexity: O(n)
168+
:param ll: an input linked list
169+
:return: true if ll is a palindrome, false otherwise
170+
"""
171+
return ll == reverse_linked_list(ll)
172+
173+
174+
class TestPalindrome(unittest.TestCase):
175+
176+
def setUp(self):
177+
self.test_cases = [
178+
(
179+
LinkedList(1, 2, 3, 4, 3, 2, 1),
180+
True
181+
),
182+
(
183+
LinkedList(1, 2, 3, 2, 1),
184+
True
185+
),
186+
(
187+
LinkedList(1, 2, 3, 4, 2, 1),
188+
False
189+
),
190+
(
191+
LinkedList(4, 5, 6, 7, 8, 9),
192+
False
193+
),
194+
(
195+
LinkedList(99, 0),
196+
False
197+
),
198+
(
199+
LinkedList(100, 100),
200+
True
201+
),
202+
(
203+
LinkedList(7),
204+
True
205+
),
206+
(
207+
LinkedList(),
208+
True
209+
)
210+
]
211+
212+
def test_palindrome(self):
213+
for ll, expected in self.test_cases:
214+
self.assertEqual(palindrome(ll), expected, msg=ll)
215+
216+
217+
if __name__ == '__main__':
218+
unittest.main()

0 commit comments

Comments
 (0)