Skip to content

Commit a165acf

Browse files
committed
refactor(linked-list): remove duplicates in singly linked lists
1 parent fb83f84 commit a165acf

File tree

3 files changed

+64
-11
lines changed

3 files changed

+64
-11
lines changed

datastructures/linked_lists/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,13 @@ def remove_tail(self):
577577
"""
578578
raise NotImplementedError("Not yet implemented")
579579

580+
@abstractmethod
581+
def remove_duplicates(self) -> Optional[Node]:
582+
"""
583+
Remotes the duplicates from a linked list
584+
"""
585+
raise NotImplementedError("Not yet implemented")
586+
580587
@abstractmethod
581588
def rotate(self, k: int):
582589
"""

datastructures/linked_lists/singly_linked_list/__init__.py

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Any, Union, Optional, Tuple
1+
from typing import Any, Union, Optional, Dict
22

33
from datastructures.stacks import Stack
44
from .node import SingleNode
@@ -440,24 +440,39 @@ def contains_cycle(self):
440440
# fast runner hit the end of the list
441441
return False
442442

443-
def remove_duplicates(self):
443+
def remove_duplicates(self) -> Optional[SingleNode]:
444444
"""
445-
Removes duplicates from linked list
445+
Removes duplicates from linked list. Uses a dictionary to keep track of seen values in the linked list.
446+
For every encountered duplicate value, it is discarded and removed from the linked list.
447+
448+
Complexity:
449+
Where n is the number of nodes in the linked list:
450+
451+
Time O(n): as this iterates through each node in the linked list checking its value against what's in the
452+
dictionary
453+
454+
Space O(n); a dictionary is used to store duplicate values in the linked list. In the worst case no duplicates
455+
exist so, the dictionary has all the values from every node in the linked list.
446456
"""
447457

448458
if self.head is None or self.head.next is None:
449459
return self.head
450460

461+
seen: Dict[Any, bool] = dict()
451462
current = self.head
452-
next_ = self.head.next
463+
previous: Optional[SingleNode] = None
453464

454-
while current and next_:
455-
if next_.data == current.data:
456-
current.next = next_.next
457-
next_ = next_.next
465+
while current:
466+
if current.data in seen:
467+
# remove node
468+
previous.next = current.next
469+
current = None
458470
else:
459-
current = current.next
460-
next_ = next_.next
471+
# add node data to seen
472+
seen[current.data] = True
473+
previous = current
474+
475+
current = previous.next
461476

462477
return self.head
463478

@@ -720,7 +735,8 @@ def reverse_list(head_node: SingleNode) -> SingleNode:
720735
# tail of previous k-group to fix our linked list pointers
721736
tail = dummy
722737

723-
# set a tracking node, tracking_node, to cycle through linked list and a head of current linked list, current_head
738+
# set a tracking node, tracking_node, to cycle through linked list and a head of current linked list,
739+
# current_head
724740
tracking_node, current_head = self.head, self.head
725741

726742
# while tracking node is tracking a node and hasn't reached end
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import unittest
2+
3+
from . import SinglyLinkedList
4+
5+
6+
class RemoveDuplicatesSinglyLinkedListTestCase(unittest.TestCase):
7+
def test_remove_duplicates_empty_list(self):
8+
"""should return None for empty list"""
9+
linked_list = SinglyLinkedList()
10+
actual = linked_list.remove_duplicates()
11+
12+
self.assertIsNone(actual)
13+
14+
def test_remove_duplicates_non_empty_list_1(self):
15+
"""Removes duplicates from [1,6,1,4,2,2,4] to become [1,6,4,2]"""
16+
linked_list = SinglyLinkedList()
17+
18+
data = [1, 6, 1, 4, 2, 2, 4]
19+
for d in data:
20+
linked_list.append(d)
21+
22+
new_head = linked_list.remove_duplicates()
23+
expected = [1, 6, 4, 2]
24+
25+
self.assertIsNotNone(new_head)
26+
self.assertEqual(expected, list(linked_list))
27+
28+
29+
if __name__ == "__main__":
30+
unittest.main()

0 commit comments

Comments
 (0)