Skip to content

Commit ac7bff5

Browse files
committed
add singly linked list along with test cases
1 parent ac6b7ba commit ac7bff5

File tree

2 files changed

+273
-0
lines changed

2 files changed

+273
-0
lines changed

linked_list/library/linked_list.py

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
""" Implementation of Singly Linked List Data Structure """
2+
3+
4+
class Node:
5+
""" Node class contains everything related to Linked List node """
6+
7+
def __init__(self, data):
8+
""" initializing single node with data """
9+
self.data = data
10+
self.next = None
11+
12+
def __repr__(self):
13+
return f"Node: data={self.data}"
14+
15+
def get_data(self):
16+
""" Return the self.data attribute. """
17+
return self.data
18+
19+
def set_data(self, new_data):
20+
""" replace the existing value of the self.data attribute with
21+
new_data parameter
22+
"""
23+
self.data = new_data
24+
25+
def get_next(self):
26+
""" return the self.next attribute. """
27+
return self.next
28+
29+
def set_next(self, new_next):
30+
""" replace the existing value of the self.next attribute with
31+
new_next parameter
32+
"""
33+
self.next = new_next
34+
35+
36+
class LinkedList:
37+
""" Singly Linked List is a linear data structure.
38+
Unlike arrays, linked list elements are not stored at a contiguous
39+
location; the elements are linked using pointers.
40+
"""
41+
42+
def __init__(self):
43+
""" initializing singly linked list with zero node """
44+
self.head = None
45+
46+
def __len__(self):
47+
""" returns the number of nodes currently present in linked list """
48+
current = self.head
49+
count = 0
50+
while current is not None:
51+
count = count + 1
52+
current = current.get_next()
53+
return count
54+
55+
def __repr__(self):
56+
return f"LinkedList: head={self.head}"
57+
58+
def get_head(self):
59+
""" returns the first linked list node """
60+
return self.head
61+
62+
def is_empty(self):
63+
""" returns true if the Linked List is empty. Otherwise, return false
64+
"""
65+
return self.__len__() == 0
66+
67+
def insert_head(self, data):
68+
""" inserts node at the start of linked list """
69+
node = Node(data)
70+
node.set_next(self.head)
71+
self.head = node
72+
73+
def insert_tail(self, data):
74+
""" inserts node at the end of linked list """
75+
node = Node(data)
76+
if self.head is None:
77+
self.head = node
78+
else:
79+
current = self.head
80+
while current.get_next() is not None:
81+
current = current.get_next()
82+
current.set_next(node)
83+
84+
def insert_at(self, data, position):
85+
""" inserts node at specified position in linked list """
86+
length = self.__len__()
87+
if position <= 1:
88+
self.insert_head(data)
89+
elif position >= length:
90+
self.insert_tail(data)
91+
else:
92+
node = Node(data)
93+
previous = self.head
94+
current = self.head
95+
for _ in range(1, position):
96+
previous = current
97+
current = current.get_next()
98+
previous.set_next(node)
99+
node.set_next(current)
100+
101+
def delete_head(self):
102+
""" removes first linked list node and returns data. Raise exception,
103+
if linkedlist is empty
104+
"""
105+
if self.head is None:
106+
raise IndexError("linkedlist is empty")
107+
node = self.head
108+
data = node.get_data()
109+
self.head = self.head.get_next()
110+
return data
111+
112+
def delete_tail(self):
113+
""" removes last linked list node and returns data. raise exception,
114+
if linkedlist is empty
115+
"""
116+
if self.head is None:
117+
raise IndexError("linkedlist is empty")
118+
current = self.head
119+
if current.get_next() is None:
120+
self.head = None
121+
else:
122+
previous = self.head
123+
while current.get_next() is not None:
124+
previous = current
125+
current = current.get_next()
126+
previous.set_next(None)
127+
return current.get_data()
128+
129+
def delete_at(self, position):
130+
""" removes specified node from linked list and returns data.
131+
raise exception, if position is invalid.
132+
"""
133+
length = self.__len__()
134+
if position < 1 or position > length:
135+
raise ValueError("invalid position")
136+
137+
if position == 1:
138+
return self.delete_head()
139+
elif position == length:
140+
return self.delete_tail()
141+
else:
142+
previous = self.head
143+
current = self.head
144+
for _ in range(1, position):
145+
previous = current
146+
current = current.get_next()
147+
removed_data = current.get_data()
148+
previous.set_next(current.get_next())
149+
current.set_next(None)
150+
return removed_data
151+
152+
def data_at(self, position):
153+
""" returns node data without removing it.
154+
raise exception, if position is invalid.
155+
"""
156+
length = self.__len__()
157+
if position < 1 or position > length:
158+
raise ValueError("invalid position")
159+
160+
current = self.head()
161+
for _ in range(1, position):
162+
current = current.get_next()
163+
return current.get_data()
164+
165+
def print(self):
166+
""" prints entire linked list without changing underlying data """
167+
current = self.head
168+
while current is not None:
169+
print(" -> ", current.get_data())
170+
current = current.get_next()
171+
print()
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
""" unittest for linked list operations """
2+
3+
import unittest
4+
from linked_list import LinkedList
5+
6+
def linkedlist_to_array(linkedlist):
7+
""" converting linkedlist data to list """
8+
result = []
9+
current = linkedlist.get_head()
10+
while current:
11+
result.append(current.get_data())
12+
current = current.get_next()
13+
return result
14+
15+
16+
class TestLinkedListEmpty(unittest.TestCase):
17+
""" tests on empty linked list """
18+
def setUp(self):
19+
""" setup empty linked list """
20+
self.linkedlist = LinkedList()
21+
22+
def test_lenght(self):
23+
""" test for linked list lenght operation """
24+
self.assertEqual(len(self.linkedlist), 0)
25+
26+
def test_get_head(self):
27+
""" test for linked list get_head operation """
28+
self.assertIsNone(self.linkedlist.head)
29+
30+
def test_is_empty(self):
31+
""" test for linked list is_empty operation """
32+
self.assertTrue(self.linkedlist.is_empty())
33+
34+
def test_insert_head(self):
35+
""" test for linked list insert_head operation """
36+
self.linkedlist.insert_head(45)
37+
self.linkedlist.insert_head(34)
38+
self.assertListEqual(linkedlist_to_array(self.linkedlist), [34, 45])
39+
40+
def test_insert_tail(self):
41+
""" test for linked list insert_tail operation """
42+
self.linkedlist.insert_tail(45)
43+
self.linkedlist.insert_tail(34)
44+
self.linkedlist.insert_tail(56)
45+
self.assertListEqual(linkedlist_to_array(self.linkedlist), [45, 34, 56])
46+
47+
48+
class TestLinkedList(unittest.TestCase):
49+
""" tests on filled linked list """
50+
def setUp(self):
51+
""" setup empty linked list """
52+
self.linkedlist = LinkedList()
53+
self.linkedlist.insert_tail(1)
54+
self.linkedlist.insert_tail(2)
55+
self.linkedlist.insert_tail(3)
56+
self.linkedlist.insert_tail(4)
57+
self.linkedlist.insert_tail(5)
58+
59+
def test_lenght(self):
60+
""" test for linked list lenght operation """
61+
self.assertEqual(len(self.linkedlist), 5)
62+
63+
def test_get_head(self):
64+
""" test for linked list get_head operation """
65+
self.assertIsNotNone(self.linkedlist.head)
66+
67+
def test_is_empty(self):
68+
""" test for linked list is_empty operation """
69+
self.assertFalse(self.linkedlist.is_empty())
70+
71+
def test_insert_head(self):
72+
""" test for linked list insert_head operation """
73+
self.linkedlist.insert_head(45)
74+
self.assertListEqual(linkedlist_to_array(self.linkedlist), [45, 1, 2, 3, 4, 5])
75+
self.linkedlist.insert_head(34)
76+
self.assertListEqual(linkedlist_to_array(self.linkedlist), [34, 45, 1, 2, 3, 4, 5])
77+
78+
def test_insert_tail(self):
79+
""" test for linked list insert_tail operation """
80+
self.linkedlist.insert_tail(45)
81+
self.assertListEqual(linkedlist_to_array(self.linkedlist), [1, 2, 3, 4, 5, 45])
82+
self.linkedlist.insert_tail(34)
83+
self.assertListEqual(linkedlist_to_array(self.linkedlist), [1, 2, 3, 4, 5, 45, 34])
84+
self.linkedlist.insert_tail(56)
85+
self.assertListEqual(linkedlist_to_array(self.linkedlist), [1, 2, 3, 4, 5, 45, 34, 56])
86+
87+
def test_insert_at(self):
88+
""" test for linked list insert_at operation """
89+
self.linkedlist.insert_at(45, 2)
90+
self.assertListEqual(linkedlist_to_array(self.linkedlist), [1, 45, 2, 3, 4, 5])
91+
self.linkedlist.insert_at(34, 4)
92+
self.assertListEqual(linkedlist_to_array(self.linkedlist), [1, 45, 2, 34, 3, 4, 5])
93+
self.linkedlist.insert_at(44, 10)
94+
self.assertListEqual(linkedlist_to_array(self.linkedlist), [1, 45, 2, 34, 3, 4, 5, 44])
95+
self.linkedlist.insert_at(23, 0)
96+
self.assertListEqual(linkedlist_to_array(self.linkedlist), [23, 1, 45, 2, 34, 3, 4, 5, 44])
97+
self.linkedlist.insert_at(22, -2)
98+
self.assertListEqual(linkedlist_to_array(self.linkedlist), [22, 23, 1, 45, 2, 34, 3, 4, 5, 44])
99+
100+
101+
if __name__ == '__main__':
102+
unittest.main()

0 commit comments

Comments
 (0)