@@ -9,50 +9,110 @@ def __repr__(self):
99
1010class LinkedList :
1111 def __init__ (self ):
12- self .head = None # initialize head to None
12+ self .head = None
13+
14+ def __iter__ (self ):
15+ node = self .head
16+ while node :
17+ yield node .data
18+ node = node .next
19+
20+ def __len__ (self ) -> int :
21+ """
22+ Return length of linked list i.e. number of nodes
23+ >>> linked_list = LinkedList()
24+ >>> len(linked_list)
25+ 0
26+ >>> linked_list.insert_tail("head")
27+ >>> len(linked_list)
28+ 1
29+ >>> linked_list.insert_head("head")
30+ >>> len(linked_list)
31+ 2
32+ >>> _ = linked_list.delete_tail()
33+ >>> len(linked_list)
34+ 1
35+ >>> _ = linked_list.delete_head()
36+ >>> len(linked_list)
37+ 0
38+ """
39+ return len (tuple (iter (self )))
40+
41+ def __repr__ (self ):
42+ """
43+ String representation/visualization of a Linked Lists
44+ """
45+ return "->" .join ([str (item ) for item in self ])
46+
47+ def __getitem__ (self , index ):
48+ """
49+ Indexing Support. Used to get a node at particular position
50+ """
51+ if index < 0 :
52+ raise ValueError ("Negative indexes are not yet supported" )
53+ for i , node in enumerate (self ):
54+ if i == index :
55+ return node .data
56+
57+ # Used to change the data of a particular node
58+ def __setitem__ (self , index , data ):
59+ current = self .head
60+ # If list is empty
61+ if current is None :
62+ raise IndexError ("The Linked List is empty" )
63+ for i in range (index ):
64+ if current .next is None :
65+ raise IndexError ("list index out of range" )
66+ current = current .next
67+ current .data = data
1368
1469 def insert_tail (self , data ) -> None :
70+ self .insert_nth (len (self ), data )
71+
72+ def insert_head (self , data ) -> None :
73+ self .insert_nth (0 , data )
74+
75+ def insert_nth (self , index : int , data ) -> None :
76+ if not 0 <= index <= len (self ):
77+ raise IndexError ("list index out of range" )
78+ new_node = Node (data )
1579 if self .head is None :
16- self .insert_head (data ) # if this is first node, call insert_head
80+ self .head = new_node
81+ elif index == 0 :
82+ new_node .next = self .head # link new_node to head
83+ self .head = new_node
1784 else :
1885 temp = self .head
19- while temp . next : # traverse to last node
86+ for _ in range ( index - 1 ):
2087 temp = temp .next
21- temp .next = Node (data ) # create node & link to tail
22-
23- def insert_head (self , data ) -> None :
24- new_node = Node (data ) # create a new node
25- if self .head :
26- new_node .next = self .head # link new_node to head
27- self .head = new_node # make NewNode as head
88+ new_node .next = temp .next
89+ temp .next = new_node
2890
2991 def print_list (self ) -> None : # print every node data
30- temp = self .head
31- while temp :
32- print (temp .data )
33- temp = temp .next
34-
35- def delete_head (self ): # delete from head
36- temp = self .head
37- if self .head :
38- self .head = self .head .next
39- temp .next = None
40- return temp
92+ print (self )
93+
94+ def delete_head (self ):
95+ return self .delete_nth (0 )
4196
4297 def delete_tail (self ): # delete from tail
43- temp = self .head
44- if self .head :
45- if self .head .next is None : # if head is the only Node in the Linked List
46- self .head = None
47- else :
48- while temp .next .next : # find the 2nd last element
49- temp = temp .next
50- # (2nd last element).next = None and temp = last element
51- temp .next , temp = None , temp .next
52- return temp
98+ return self .delete_nth (len (self ) - 1 )
99+
100+ def delete_nth (self , index : int = 0 ):
101+ if not 0 <= index <= len (self ) - 1 : # test if index is valid
102+ raise IndexError ("list index out of range" )
103+ delete_node = self .head # default first node
104+ if index == 0 :
105+ self .head = self .head .next
106+ else :
107+ temp = self .head
108+ for _ in range (index - 1 ):
109+ temp = temp .next
110+ delete_node = temp .next
111+ temp .next = temp .next .next
112+ return delete_node .data
53113
54114 def is_empty (self ) -> bool :
55- return self .head is None # return True if head is none
115+ return self .head is None
56116
57117 def reverse (self ):
58118 prev = None
@@ -70,101 +130,74 @@ def reverse(self):
70130 # Return prev in order to put the head at the end
71131 self .head = prev
72132
73- def __repr__ (self ): # String representation/visualization of a Linked Lists
74- current = self .head
75- string_repr = ""
76- while current :
77- string_repr += f"{ current } --> "
78- current = current .next
79- # END represents end of the LinkedList
80- return string_repr + "END"
81133
82- # Indexing Support. Used to get a node at particular position
83- def __getitem__ (self , index ):
84- current = self .head
134+ def test_singly_linked_list () -> None :
135+ """
136+ >>> test_singly_linked_list()
137+ """
138+ linked_list = LinkedList ()
139+ assert linked_list .is_empty () is True
140+ assert str (linked_list ) == ""
85141
86- # If LinkedList is empty
87- if current is None :
88- raise IndexError ("The Linked List is empty" )
142+ try :
143+ linked_list .delete_head ()
144+ assert False # This should not happen.
145+ except IndexError :
146+ assert True # This should happen.
89147
90- # Move Forward 'index' times
91- for _ in range (index ):
92- # If the LinkedList ends before reaching specified node
93- if current .next is None :
94- raise IndexError ("Index out of range." )
95- current = current .next
96- return current
148+ try :
149+ linked_list .delete_tail ()
150+ assert False # This should not happen.
151+ except IndexError :
152+ assert True # This should happen.
97153
98- # Used to change the data of a particular node
99- def __setitem__ (self , index , data ):
100- current = self .head
101- # If list is empty
102- if current is None :
103- raise IndexError ("The Linked List is empty" )
104- for i in range (index ):
105- if current .next is None :
106- raise IndexError ("Index out of range." )
107- current = current .next
108- current .data = data
154+ for i in range (10 ):
155+ assert len (linked_list ) == i
156+ linked_list .insert_nth (i , i + 1 )
157+ assert str (linked_list ) == "->" .join (str (i ) for i in range (1 , 11 ))
109158
110- def __len__ (self ):
111- """
112- Return length of linked list i.e. number of nodes
113- >>> linked_list = LinkedList()
114- >>> len(linked_list)
115- 0
116- >>> linked_list.insert_tail("head")
117- >>> len(linked_list)
118- 1
119- >>> linked_list.insert_head("head")
120- >>> len(linked_list)
121- 2
122- >>> _ = linked_list.delete_tail()
123- >>> len(linked_list)
124- 1
125- >>> _ = linked_list.delete_head()
126- >>> len(linked_list)
127- 0
128- """
129- if not self .head :
130- return 0
159+ linked_list .insert_head (0 )
160+ linked_list .insert_tail (11 )
161+ assert str (linked_list ) == "->" .join (str (i ) for i in range (0 , 12 ))
131162
132- count = 0
133- cur_node = self .head
134- while cur_node .next :
135- count += 1
136- cur_node = cur_node .next
137- return count + 1
163+ assert linked_list .delete_head () == 0
164+ assert linked_list .delete_nth (9 ) == 10
165+ assert linked_list .delete_tail () == 11
166+ assert str (linked_list ) == "->" .join (str (i ) for i in range (1 , 10 ))
138167
139168
140169def main ():
141- A = LinkedList ()
142- A .insert_head (input ("Inserting 1st at head " ).strip ())
143- A .insert_head (input ("Inserting 2nd at head " ).strip ())
170+ from doctest import testmod
171+
172+ testmod ()
173+
174+ linked_list = LinkedList ()
175+ linked_list .insert_head (input ("Inserting 1st at head " ).strip ())
176+ linked_list .insert_head (input ("Inserting 2nd at head " ).strip ())
144177 print ("\n Print list:" )
145- A .print_list ()
146- A .insert_tail (input ("\n Inserting 1st at tail " ).strip ())
147- A .insert_tail (input ("Inserting 2nd at tail " ).strip ())
178+ linked_list .print_list ()
179+ linked_list .insert_tail (input ("\n Inserting 1st at tail " ).strip ())
180+ linked_list .insert_tail (input ("Inserting 2nd at tail " ).strip ())
148181 print ("\n Print list:" )
149- A .print_list ()
182+ linked_list .print_list ()
150183 print ("\n Delete head" )
151- A .delete_head ()
184+ linked_list .delete_head ()
152185 print ("Delete tail" )
153- A .delete_tail ()
186+ linked_list .delete_tail ()
154187 print ("\n Print list:" )
155- A .print_list ()
188+ linked_list .print_list ()
156189 print ("\n Reverse linked list" )
157- A .reverse ()
190+ linked_list .reverse ()
158191 print ("\n Print list:" )
159- A .print_list ()
192+ linked_list .print_list ()
160193 print ("\n String representation of linked list:" )
161- print (A )
194+ print (linked_list )
162195 print ("\n Reading/changing Node data using indexing:" )
163- print (f"Element at Position 1: { A [1 ]} " )
164- A [1 ] = input ("Enter New Value: " ).strip ()
196+ print (f"Element at Position 1: { linked_list [1 ]} " )
197+ linked_list [1 ] = input ("Enter New Value: " ).strip ()
165198 print ("New list:" )
166- print (A )
167- print (f"length of A is : { len (A )} " )
199+ print (linked_list )
200+ print (f"length of linked_list is : { len (linked_list )} " )
168201
169202
170203if __name__ == "__main__" :
0 commit comments