Skip to content

Commit 9b8a6d4

Browse files
committed
refactor(data-structures, linked list): is palindrome test cases modification
1 parent b75bb86 commit 9b8a6d4

File tree

4 files changed

+64
-210
lines changed

4 files changed

+64
-210
lines changed

datastructures/linked_lists/mergeklinkedlists/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from typing import List, Optional
2-
from .. import Node
2+
from datastructures.linked_lists import Node
33

44

55
def merge_two_sorted_lists(
@@ -15,7 +15,7 @@ def merge_two_sorted_lists(
1515
This is because both linked lists have to be traversed to merge into a single linked list
1616
1717
Space O(n+m) where n & m are the number of nodes in the respective linked list. This is because a new linked list
18-
is created from both linked lists and the nodes from each are merged into a single linke list.
18+
is created from both linked lists and the nodes from each are merged into a single linked list.
1919
2020
Args:
2121
head_one(Node): optional head node of linked list one

datastructures/linked_lists/mergeklinkedlists/test_merge.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import unittest
2-
from . import merge_two_sorted_lists, merge_k_lists
3-
from ..singly_linked_list import SinglyLinkedList
2+
from datastructures.linked_lists.mergeklinkedlists import (
3+
merge_two_sorted_lists,
4+
merge_k_lists,
5+
)
6+
from datastructures.linked_lists.singly_linked_list import SinglyLinkedList
47

58

69
class MergeSortedLinkedListsTestCase(unittest.TestCase):

datastructures/linked_lists/singly_linked_list/single_linked_list.py

Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,11 @@ def delete_node(self, single_node: SingleNode):
153153
if not self.head:
154154
return
155155

156+
# if the node we are deleting is the head node
157+
if self.head == single_node:
158+
self.head = self.head.next
159+
return
160+
156161
current_node = self.head
157162
previous_node = None
158163
while current_node is not None:
@@ -542,11 +547,26 @@ def display_backward(self):
542547
def display_forward(self):
543548
pass
544549

545-
def contains_cycle(self):
550+
def contains_cycle(self) -> bool:
546551
"""
547-
Check if the SinglyLinkedList contains a cycle
548-
:return:
552+
Check if the SinglyLinkedList contains a cycle, This uses floyd's cycle detection algorithm to check if a linked
553+
list contains a cycle. A cycle in a linked list is determined by a node in the linked list having its next pointer
554+
point to a node in the same liked list.
555+
556+
Complexity Analysis:
557+
n is the number of nodes in the linked list
558+
559+
Time: O(n), we traverse the nodes in the linked list iteratively, at the worst case, we traverse all the nodes
560+
in the linked list until we reach the end of the linked list
561+
562+
Space: O(1) as there is no extra space used other than the pointers used to traverse the linked list.
563+
564+
Returns:
565+
bool: True if the linked list contains a cycle, False otherwise
549566
"""
567+
if not self.head or not self.head.next:
568+
return False
569+
550570
fast_runner = self.head
551571
slow_runner = self.head
552572

@@ -655,11 +675,8 @@ def is_palindrome(self) -> bool:
655675
bool: True if the linked list is a palindrome, false otherwise.
656676
"""
657677

658-
if not self.head:
659-
return False
660-
661-
# A LinkedList with 1 Node is a Palindrome
662-
if not self.head.next:
678+
# an empty linked list is consided a palindrome
679+
if not self.head or not self.head.next:
663680
return True
664681

665682
current = self.head
@@ -682,17 +699,18 @@ def is_palindrome(self) -> bool:
682699

683700
def is_palindrome_2(self) -> bool:
684701
"""
685-
Checks to see if a Linked list is a Palindrome.
686-
Returns True if it is, false otherwise.
702+
Checks to see if a Linked list is a Palindrome. Returns True if it is, false otherwise.
703+
687704
Uses two pointers approach to check if a linked list is a palindrome. First it finds the middle of the list using
688705
two pointers a fast and a slow pointer and then reverses the second half of the list. Once the second half is
689706
reversed, it compares the first half and the reversed second half
690707
708+
This modifies the linked list
709+
691710
Complexity:
692711
We assume that n is the number of nodes in the linked list
693712
694713
Time O(n): we traverse the linked list to check for the palindrome property.
695-
696714
Space O(1): No extra space is used when traversing the linked list
697715
698716
Returns:
@@ -703,20 +721,12 @@ def is_palindrome_2(self) -> bool:
703721
if not self.head or not self.head.next:
704722
return True
705723

706-
# find the middle of the list using fast and slow pointers. The fast pointer will have gotten to the end of the
724+
# Find the middle of the list using fast and slow pointers. The fast pointer will have gotten to the end of the
707725
# the linked list and the slow pointer will be at the middle of the linked list
708-
slow, fast = self.head, self.head
709-
while fast and fast.next:
710-
slow = slow.next
711-
fast = fast.next.next
726+
middle_node = self.middle_node()
712727

713728
# reverse the second half of the list
714-
prev = None
715-
while slow:
716-
nxt = slow.next
717-
slow.next = prev
718-
prev = slow
719-
slow = nxt
729+
prev = self.reverse_list(middle_node)
720730

721731
# now prev is the head of the reversed second half
722732
# compare the first half and the reversed second half

datastructures/linked_lists/singly_linked_list/test_singly_linked_palindrome.py

Lines changed: 25 additions & 184 deletions
Original file line numberDiff line numberDiff line change
@@ -1,205 +1,46 @@
11
import unittest
2-
2+
from typing import List, Any
3+
from parameterized import parameterized
34
import pytest
45

5-
from . import SinglyLinkedList
6-
7-
8-
class LinkedListIsPalindromeTestCase(unittest.TestCase):
9-
def test_1(self):
10-
"""should return true for ["r", "a", "c", "e", "c", "a", "r"]"""
11-
linked_list = SinglyLinkedList()
12-
data = ["r", "a", "c", "e", "c", "a", "r"]
13-
for d in data:
14-
linked_list.append(d)
15-
16-
actual = linked_list.is_palindrome()
17-
self.assertTrue(actual)
18-
19-
def test_2(self):
20-
"""should return true for [2, 4, 6, 4, 2]"""
21-
linked_list = SinglyLinkedList()
22-
data = [2, 4, 6, 4, 2]
23-
for d in data:
24-
linked_list.append(d)
25-
26-
actual = linked_list.is_palindrome()
27-
self.assertTrue(actual)
28-
29-
def test_3(self):
30-
"""should return true for [0, 3, 5, 5, 0]"""
31-
linked_list = SinglyLinkedList()
32-
data = [0, 3, 5, 5, 0]
33-
for d in data:
34-
linked_list.append(d)
35-
36-
actual = linked_list.is_palindrome()
37-
self.assertFalse(actual)
38-
39-
def test_4(self):
40-
"""should return true for [9, 7, 4, 4, 7, 9]"""
41-
linked_list = SinglyLinkedList()
42-
data = [9, 7, 4, 4, 7, 9]
43-
for d in data:
44-
linked_list.append(d)
45-
46-
actual = linked_list.is_palindrome()
47-
self.assertTrue(actual)
48-
49-
def test_5(self):
50-
"""should return true for [1,2,3,2,1]"""
51-
linked_list = SinglyLinkedList()
52-
data = [1, 2, 3, 2, 1]
53-
for d in data:
54-
linked_list.append(d)
55-
56-
actual = linked_list.is_palindrome()
57-
self.assertTrue(actual)
58-
59-
def test_6(self):
60-
"""should return true for [4,7,9,5,4]"""
61-
linked_list = SinglyLinkedList()
62-
data = [4, 7, 9, 5, 4]
63-
for d in data:
64-
linked_list.append(d)
6+
from datastructures.linked_lists.singly_linked_list import SinglyLinkedList
657

66-
actual = linked_list.is_palindrome()
67-
self.assertFalse(actual)
8+
IS_LINKED_LIST_PALINDROME_TEST_CASES = [
9+
(["r", "a", "c", "e", "c", "a", "r"], True),
10+
([2, 4, 6, 4, 2], True),
11+
([0, 3, 5, 5, 0], False),
12+
([9, 7, 4, 4, 7, 9], True),
13+
([1, 2, 3, 2, 1], True),
14+
([4, 7, 9, 5, 4], False),
15+
([2, 3, 5, 5, 3, 2], True),
16+
([6, 1, 0, 5, 1, 6], False),
17+
([3, 6, 9, 8, 4, 8, 9, 6, 3], True),
18+
([1, 2], False),
19+
([1, 2, 3, 4, 5], False),
20+
]
6821

69-
def test_7(self):
70-
"""should return true for [2,3,5,5,3,2]"""
71-
linked_list = SinglyLinkedList()
72-
data = [2, 3, 5, 5, 3, 2]
73-
for d in data:
74-
linked_list.append(d)
7522

76-
actual = linked_list.is_palindrome()
77-
self.assertTrue(actual)
78-
79-
def test_8(self):
80-
"""should return true for [6,1,0,5,1,6]"""
81-
linked_list = SinglyLinkedList()
82-
data = [6, 1, 0, 5, 1, 6]
83-
for d in data:
84-
linked_list.append(d)
85-
86-
actual = linked_list.is_palindrome()
87-
self.assertFalse(actual)
88-
89-
def test_9(self):
90-
"""should return true for [3,6,9,8,4,8,9,6,3]"""
23+
class LinkedListIsPalindromeTestCase(unittest.TestCase):
24+
@parameterized.expand(IS_LINKED_LIST_PALINDROME_TEST_CASES)
25+
def test_is_palindrome_using_stack(self, data: List[Any], expected: bool):
26+
"""Tests to check if a linked list is a palindrome using a stack approach"""
9127
linked_list = SinglyLinkedList()
92-
data = [3, 6, 9, 8, 4, 8, 9, 6, 3]
9328
for d in data:
9429
linked_list.append(d)
9530

9631
actual = linked_list.is_palindrome()
97-
self.assertTrue(actual)
98-
99-
100-
class LinkedListIsPalindromeV2TestCase(unittest.TestCase):
101-
"""
102-
Tests to check if a linked list is a palindrome using fast and slow pointers approach
103-
"""
104-
105-
@pytest.mark.linked_list_is_palindrome_fast_and_slow_pointer
106-
def test_1(self):
107-
"""should return true for ["r", "a", "c", "e", "c", "a", "r"]"""
108-
linked_list = SinglyLinkedList()
109-
data = ["r", "a", "c", "e", "c", "a", "r"]
110-
for d in data:
111-
linked_list.append(d)
112-
113-
actual = linked_list.is_palindrome_2()
114-
self.assertTrue(actual)
115-
116-
@pytest.mark.linked_list_is_palindrome_fast_and_slow_pointer
117-
def test_2(self):
118-
"""should return true for [2, 4, 6, 4, 2]"""
119-
linked_list = SinglyLinkedList()
120-
data = [2, 4, 6, 4, 2]
121-
for d in data:
122-
linked_list.append(d)
123-
124-
actual = linked_list.is_palindrome_2()
125-
self.assertTrue(actual)
126-
127-
@pytest.mark.linked_list_is_palindrome_fast_and_slow_pointer
128-
def test_3(self):
129-
"""should return true for [0, 3, 5, 5, 0]"""
130-
linked_list = SinglyLinkedList()
131-
data = [0, 3, 5, 5, 0]
132-
for d in data:
133-
linked_list.append(d)
134-
135-
actual = linked_list.is_palindrome_2()
136-
self.assertFalse(actual)
137-
138-
@pytest.mark.linked_list_is_palindrome_fast_and_slow_pointer
139-
def test_4(self):
140-
"""should return true for [9, 7, 4, 4, 7, 9]"""
141-
linked_list = SinglyLinkedList()
142-
data = [9, 7, 4, 4, 7, 9]
143-
for d in data:
144-
linked_list.append(d)
145-
146-
actual = linked_list.is_palindrome_2()
147-
self.assertTrue(actual)
148-
149-
@pytest.mark.linked_list_is_palindrome_fast_and_slow_pointer
150-
def test_5(self):
151-
"""should return true for [1,2,3,2,1]"""
152-
linked_list = SinglyLinkedList()
153-
data = [1, 2, 3, 2, 1]
154-
for d in data:
155-
linked_list.append(d)
156-
157-
actual = linked_list.is_palindrome_2()
158-
self.assertTrue(actual)
159-
160-
@pytest.mark.linked_list_is_palindrome_fast_and_slow_pointer
161-
def test_6(self):
162-
"""should return true for [4,7,9,5,4]"""
163-
linked_list = SinglyLinkedList()
164-
data = [4, 7, 9, 5, 4]
165-
for d in data:
166-
linked_list.append(d)
167-
168-
actual = linked_list.is_palindrome_2()
169-
self.assertFalse(actual)
170-
171-
@pytest.mark.linked_list_is_palindrome_fast_and_slow_pointer
172-
def test_7(self):
173-
"""should return true for [2,3,5,5,3,2]"""
174-
linked_list = SinglyLinkedList()
175-
data = [2, 3, 5, 5, 3, 2]
176-
for d in data:
177-
linked_list.append(d)
178-
179-
actual = linked_list.is_palindrome_2()
180-
self.assertTrue(actual)
181-
182-
@pytest.mark.linked_list_is_palindrome_fast_and_slow_pointer
183-
def test_8(self):
184-
"""should return false for [6,1,0,5,1,6]"""
185-
linked_list = SinglyLinkedList()
186-
data = [6, 1, 0, 5, 1, 6]
187-
for d in data:
188-
linked_list.append(d)
189-
190-
actual = linked_list.is_palindrome_2()
191-
self.assertFalse(actual)
32+
self.assertEqual(expected, actual)
19233

19334
@pytest.mark.linked_list_is_palindrome_fast_and_slow_pointer
194-
def test_9(self):
195-
"""should return true for [3,6,9,8,4,8,9,6,3]"""
35+
@parameterized.expand(IS_LINKED_LIST_PALINDROME_TEST_CASES)
36+
def test_is_palindrome_using_two_pointers(self, data: List[Any], expected: bool):
37+
"""Tests to check if a linked list is a palindrome using fast and slow pointers approach"""
19638
linked_list = SinglyLinkedList()
197-
data = [3, 6, 9, 8, 4, 8, 9, 6, 3]
19839
for d in data:
19940
linked_list.append(d)
20041

20142
actual = linked_list.is_palindrome_2()
202-
self.assertTrue(actual)
43+
self.assertEqual(expected, actual)
20344

20445

20546
if __name__ == "__main__":

0 commit comments

Comments
 (0)