-
Notifications
You must be signed in to change notification settings - Fork 22
/
linkedlist.py
193 lines (170 loc) · 7.35 KB
/
linkedlist.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
#!python
from __future__ import print_function
class Node(object):
def __init__(self, data):
"""Initialize this node with the given data"""
self.data = data
self.next = None
def __repr__(self):
"""Return a string representation of this node"""
return 'Node({})'.format(repr(self.data))
class LinkedList(object):
def __init__(self, iterable=None):
"""Initialize this linked list; append the given items, if any"""
self.head = None
self.tail = None
if iterable:
for item in iterable:
self.append(item)
def __str__(self):
"""Return a formatted string representation of this linked list"""
items = ['({})'.format(repr(item)) for item in self.items()]
return '[{}]'.format(' -> '.join(items))
def __repr__(self):
"""Return a string representation of this linked list"""
return 'LinkedList({})'.format(repr(self.items()))
def items(self):
"""Return a list of all items in this linked list.
Best and worst case running time: Theta(n) for n items in the list
because we always need to loop through all n nodes."""
# Create an empty list of results
result = [] # Constant time to create a new list
# Start at the head node
current = self.head # Constant time to assign a variable reference
# Loop until the current node is None, which is one node past the tail
while current is not None: # Always n iterations because no early exit
# Append the current node's data to the results list
result.append(current.data) # Constant time to append to a list
# Skip to the next node
current = current.next # Constant time to reassign a variable
# Now result contains the data from all nodes
return result # Constant time to return a list
def is_empty(self):
"""Return True if this linked list is empty, or False"""
return self.head is None
def length(self):
"""Return the length of this linked list by traversing its nodes"""
# Node counter initialized to zero
node_count = 0
# Start at the head node
current = self.head
# Loop until the current node is None, which is one node past the tail
while current is not None:
# Count one for this node
node_count += 1
# Skip to the next node
current = current.next
# Now node_count contains the number of nodes
return node_count
def append(self, item):
"""Insert the given item at the tail of this linked list"""
# Create a new node to hold the given item
new_node = Node(item)
# Check if this linked list is empty
if self.is_empty():
# Assign head to new node
self.head = new_node
else:
# Otherwise insert new node after tail
self.tail.next = new_node
# Update tail to new node regardless
self.tail = new_node
def prepend(self, item):
"""Insert the given item at the head of this linked list"""
# Create a new node to hold the given item
new_node = Node(item)
# Check if this linked list is empty
if self.is_empty():
# Assign tail to new node
self.tail = new_node
else:
# Otherwise insert new node before head
new_node.next = self.head
# Update head to new node regardless
self.head = new_node
def delete(self, item):
"""Delete the given item from this linked list, or raise ValueError"""
# Start at the head node
current = self.head
# Keep track of the node before the one containing the given tiem
previous = None
# Create a flag to track if we have found the given item
found = False
# Loop until we have found the given item or the current node is None
while not found and current is not None:
# Check if the current node's data matches the given item
if current.data == item:
# We found data matching the given item, so update found flag
found = True
else:
# Skip to the next node
previous = current
current = current.next
# Check if we found the given item or we never did and reached the tail
if found:
# Check if we found a node in the middle of this linked list
if current is not self.head and current is not self.tail:
# Update the previous node to skip around the found node
previous.next = current.next
# Unlink the found node from its next node
current.next = None
# Check if we found a node at the head
if current is self.head:
# Update head to the next node
self.head = current.next
# Unlink the found node from the next node
current.next = None
# Check if we found a node at the tail
if current is self.tail:
# Check if there is a node before the found node
if previous is not None:
# Unlink the previous node from the found node
previous.next = None
# Update tail to the previous node regardless
self.tail = previous
else:
# Otherwise raise an error to tell the user that delete has failed
raise ValueError('Item not found: {}'.format(item))
def find(self, quality):
"""Return an item from this linked list satisfying the given quality.
Best case running time: Omega(1) if item is near the head of the list.
Worst case running time: O(n) if item is near the tail of the list or
not present and we need to loop through all n nodes in the list."""
# Start at the head node
current = self.head # Constant time to assign a variable reference
# Loop until the current node is None, which is one node past the tail
while current is not None: # Up to n iterations if we don't exit early
# Check if the current node's data satisfyies the quality function
if quality(current.data): # Constant time to call quality function
# We found data satisfying the quality function, so exit early
return current.data # Constant time to return data
# Skip to the next node
current = current.next # Constant time to reassign a variable
# We never found data satisfying quality, but have to return something
return None # Constant time to return None
def test_linked_list():
ll = LinkedList()
print(ll)
print('Appending items:')
ll.append('A')
print(ll)
ll.append('B')
print(ll)
ll.append('C')
print(ll)
print('head: ' + str(ll.head))
print('tail: ' + str(ll.tail))
print('length: ' + str(ll.length()))
# Enable this after implementing delete:
print('Deleting items:')
ll.delete('B')
print(ll)
ll.delete('C')
print(ll)
ll.delete('A')
print(ll)
print('head: ' + str(ll.head))
print('tail: ' + str(ll.tail))
print('length: ' + str(ll.length()))
if __name__ == '__main__':
test_linked_list()