@@ -9,50 +9,110 @@ def __repr__(self):
9
9
10
10
class LinkedList :
11
11
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
13
68
14
69
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 )
15
79
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
17
84
else :
18
85
temp = self .head
19
- while temp . next : # traverse to last node
86
+ for _ in range ( index - 1 ):
20
87
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
28
90
29
91
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 )
41
96
42
97
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
53
113
54
114
def is_empty (self ) -> bool :
55
- return self .head is None # return True if head is none
115
+ return self .head is None
56
116
57
117
def reverse (self ):
58
118
prev = None
@@ -70,101 +130,74 @@ def reverse(self):
70
130
# Return prev in order to put the head at the end
71
131
self .head = prev
72
132
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"
81
133
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 ) == ""
85
141
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.
89
147
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.
97
153
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 ))
109
158
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 ))
131
162
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 ))
138
167
139
168
140
169
def 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 ())
144
177
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 ())
148
181
print ("\n Print list:" )
149
- A .print_list ()
182
+ linked_list .print_list ()
150
183
print ("\n Delete head" )
151
- A .delete_head ()
184
+ linked_list .delete_head ()
152
185
print ("Delete tail" )
153
- A .delete_tail ()
186
+ linked_list .delete_tail ()
154
187
print ("\n Print list:" )
155
- A .print_list ()
188
+ linked_list .print_list ()
156
189
print ("\n Reverse linked list" )
157
- A .reverse ()
190
+ linked_list .reverse ()
158
191
print ("\n Print list:" )
159
- A .print_list ()
192
+ linked_list .print_list ()
160
193
print ("\n String representation of linked list:" )
161
- print (A )
194
+ print (linked_list )
162
195
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 ()
165
198
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 )} " )
168
201
169
202
170
203
if __name__ == "__main__" :
0 commit comments