Skip to content

Commit f49d803

Browse files
authored
Merge pull request #40 from miguelHx/miguel_2.4_partition
Miguel 2.4 - Partition [Python]
2 parents 653786f + e84533c commit f49d803

File tree

1 file changed

+274
-0
lines changed

1 file changed

+274
-0
lines changed
Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
"""
2+
Python version 3.7.0
3+
2.4 - Partition
4+
Write code to partition a linked list around a value x,
5+
such that all nodes less than x come before all nodes
6+
greater than or equal to x. If x is contained within
7+
the list, the values of x only need to be after
8+
the elements less than x. The partition element x
9+
can appear anywhere in the 'right partition'; it does
10+
not need to appear between the left and right
11+
partitions.
12+
EXAMPLE
13+
Input: 3 -> 5 -> 8 -> 5 -> 10 -> 2 -> 1 [pivot = 5]
14+
Result: 3 -> 1 -> 2 -> 10 -> 5 -> 5 -> 8
15+
"""
16+
import unittest
17+
18+
19+
class Node:
20+
def __init__(self, d: int):
21+
self.data = d
22+
self.next = None
23+
24+
def __repr__(self):
25+
return self.__str__()
26+
27+
def __str__(self):
28+
return '<Node Value: {}>'.format(self.data)
29+
30+
def __eq__(self, other: object):
31+
if not isinstance(other, Node):
32+
return NotImplemented
33+
return self.data == other.data
34+
35+
36+
class LinkedList:
37+
def __init__(self, *numbers: int):
38+
self.head = None
39+
self.tail = None
40+
self.size = 0
41+
for num in numbers:
42+
self.append_to_tail(num)
43+
44+
def append_to_tail(self, e) -> None:
45+
if isinstance(e, int):
46+
self._append_num(e)
47+
elif isinstance(e, Node):
48+
self._append_node(e)
49+
50+
def _append_num(self, d: int) -> None:
51+
if self.head is None:
52+
self.head = Node(d)
53+
self.tail = self.head
54+
else:
55+
end = Node(d)
56+
self.tail.next = end
57+
self.tail = end
58+
self.size += 1
59+
60+
def _append_node(self, n: Node) -> None:
61+
if self.head is None:
62+
self.head = n
63+
self.tail = self.head
64+
else:
65+
end = n
66+
self.tail.next = end
67+
self.tail = end
68+
self.size += 1
69+
70+
def append_to_head(self, d: int) -> None:
71+
new_head = Node(d)
72+
new_head.next = self.head
73+
self.head = new_head
74+
self.size += 1
75+
76+
def get_node_at(self, index: int) -> Node:
77+
if index < 0 or index >= self.size:
78+
raise IndexError('list index out of range')
79+
n = self.head
80+
for i in range(self.size):
81+
if i == index:
82+
return n
83+
n = n.next
84+
85+
def pop_head(self) -> Node:
86+
if self.head is None:
87+
raise IndexError('no head to pop')
88+
h = self.head
89+
h.next = None
90+
self.head = self.head.next
91+
self.size -= 1
92+
return h
93+
94+
def append(self, ll: 'LinkedList') -> None:
95+
self.tail.next = ll.head
96+
self.size += ll.size
97+
ll.head = None
98+
ll.size = 0
99+
100+
def __repr__(self):
101+
return self.__str__()
102+
103+
def __str__(self):
104+
if self.head is None:
105+
return '<empty>'
106+
ll = []
107+
n = self.head
108+
while n.next is not None:
109+
ll.append('{} -> '.format(n.data))
110+
n = n.next
111+
ll.append(str(n.data))
112+
return ''.join(ll)
113+
114+
def __eq__(self, other: object):
115+
if not isinstance(other, LinkedList):
116+
return NotImplemented
117+
a = self.head
118+
b = other.head
119+
while a is not None and b is not None:
120+
if a.data != b.data:
121+
return False
122+
# otherwise, advance both pointers
123+
a = a.next
124+
b = b.next
125+
return a is None and b is None
126+
127+
128+
def check_partitioned(ll: LinkedList, pivot: int) -> bool:
129+
"""
130+
Will check if a linked list is partitioned
131+
around a value such that all values less
132+
then the partition value come before all values
133+
greater than or equal to the partition value
134+
in the linked list.
135+
Order doesn't matter, as long as the values
136+
< partition come before values >= partition.
137+
Runtime: O(n)
138+
Space Complexity: O(1)
139+
:param ll: an input linked list
140+
:param pivot: a number to partition around
141+
:return:
142+
"""
143+
# counter for keeping track of how many times
144+
# we change from < pivot to >= pivot
145+
n = ll.head
146+
while n is not None:
147+
if n.data >= pivot:
148+
break
149+
n = n.next
150+
while n is not None:
151+
if n.data < pivot:
152+
return False
153+
n = n.next
154+
return True
155+
156+
157+
def partition_ll(ll: LinkedList, pivot: int) -> LinkedList:
158+
"""
159+
This function will take in a linked list, a
160+
pivot value, and will partition a linked
161+
list around a value x such that all nodes
162+
less than x come before all nodes greater than
163+
or equal to x. If x is in the list, then
164+
the value(s) of x only need to be after the
165+
elements less than x. They do not need to appear
166+
in between the left and right partitions.
167+
Runtime: O(n)
168+
Space Complexity: O(1)
169+
:param ll: an input linked list
170+
:param pivot: a number to partition around
171+
:return: a linked list that is 'partitioned'
172+
"""
173+
left_partition = LinkedList() # will contain values < pivot
174+
right_partition = LinkedList() # will contain values >= pivot
175+
176+
n = ll.head
177+
while n is not None:
178+
if n.data < pivot:
179+
left_partition.append_to_tail(ll.pop_head())
180+
else:
181+
right_partition.append_to_tail(ll.pop_head())
182+
n = n.next
183+
# then, merge left and right partition lists into one linked list
184+
left_partition.append(right_partition)
185+
return left_partition
186+
187+
188+
class TestPartition(unittest.TestCase):
189+
190+
def setUp(self):
191+
self.test_cases = [
192+
(
193+
LinkedList(3, 5, 8, 5, 10, 2, 1),
194+
5
195+
),
196+
(
197+
LinkedList(1, 2, 3, 4, 5, 6, 7, 8),
198+
5
199+
),
200+
(
201+
LinkedList(1, 9, 2, 5, 20, 11, 12),
202+
10
203+
),
204+
(
205+
LinkedList(1),
206+
200
207+
)
208+
]
209+
210+
self.check_partitioned_test_cases = [
211+
(
212+
LinkedList(3, 2, 1, 5, 8, 5, 10),
213+
5,
214+
True
215+
),
216+
(
217+
LinkedList(3, 1, 2, 10, 5, 5, 8),
218+
5,
219+
True
220+
),
221+
(
222+
LinkedList(3, 5, 8, 5, 10, 2, 1),
223+
5,
224+
False
225+
),
226+
(
227+
LinkedList(10, 9, 8, 3, 2, 200, 201),
228+
5,
229+
False
230+
),
231+
(
232+
LinkedList(5, 6, 7, 8, 9, 10),
233+
5,
234+
True
235+
),
236+
(
237+
LinkedList(10),
238+
5,
239+
True
240+
),
241+
(
242+
LinkedList(10, 9, 8, 7, 6, 5, 4, 3, 2, 1),
243+
1,
244+
True
245+
),
246+
(
247+
LinkedList(10, 9, 8, 7, 6, 5, 4, 3, 2, 1),
248+
5,
249+
False
250+
),
251+
(
252+
LinkedList(1, 2, 3, 4, 5, 6, 7, 8, 9),
253+
5,
254+
True
255+
),
256+
(
257+
LinkedList(5, 1),
258+
5,
259+
False
260+
),
261+
]
262+
263+
def test_check_partitioned(self):
264+
for ll, pivot, output in self.check_partitioned_test_cases:
265+
self.assertEqual(check_partitioned(ll, pivot), output, msg=(ll, pivot))
266+
267+
def test_partition(self):
268+
for ll, pivot in self.test_cases:
269+
partitioned_ll = partition_ll(ll, pivot)
270+
self.assertTrue(check_partitioned(partitioned_ll, pivot), msg=(ll, pivot))
271+
272+
273+
if __name__ == '__main__':
274+
unittest.main()

0 commit comments

Comments
 (0)