I wanted to ask for code review for DoublyLinkedList.
Especially my insert
, find
, replace
, at_index
I have the following python implementation that handles most of the cases: And I also include my unit testing. I ran against the unit test, and it passed all 12 tests.
class Node(object):
def __init__(self, data):
"""Initialize this node with the given data"""
self.data = data
self.next = None
self.last = None
def __repr__(self):
"""Return a string rxepresentation of this node"""
return 'Node({})'.format(repr(self.data))
class DoublyLinkedList(object):
def __init__(self, iterable=None):
"""Initialize this linked list; append the given items, if any"""
self.head = None
self.tail = None
self.count = 0
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 'DoublyLinkedList({})'.format(repr(self.items()))
def items(self):
"""Return a list of adll items in this linked list"""
result = []
current = self.head
while current is not None:
result.append(current.data)
current = current.next
return result
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"""
return self.count
def append(self, item):
"""Insert the given item at the tail of this linked list"""
new_node = Node(item)
self.count += 1
if self.head is None:
self.head = new_node
else:
self.tail.next = new_node
new_node.last = self.tail
self.tail = new_node
def prepend(self, item):
"""Insert the given item at the head of this linked list"""
new_node = Node(item)
self.count += 1
if self.head is None:
self.head = new_node
self.tail = self.head
else:
new_node.next = self.head
self.head = new_node
def delete(self, item):
"""Delete the given item from this linked list, or raise ValueError"""
last = None
current_node = self.head
while current_node is not None:
# The current node is the ones we are looking for
if current_node.data == item:
# Our tail is our current node
if self.tail == current_node:
self.tail = last
if last is None:
# If we are the head. We set the new head to the next value.
self.head = current_node.next
else:
# We aint the head so we set the last nodes head to the next node
last.next = current_node.next
if current_node.next is not None:
current_node.next.last = last
self.count -= 1
return # Stop checking. Don't return an error
last = current_node
current_node = current_node.next
raise ValueError
def size(self):
""" Gets the size of the Linked List
AVERAGE: O(1)
"""
return self.count
def _at_index(self, index):
""" Helper method used to get the node at an index
"""
next_node = self.head
while index > -1 or next_node is not None:
if index == 0:
return next_node
next_node = next_node.next
index -= 1
return None
def at_index(self, index):
""" Gets data at an index
"""
at_index = self._at_index(index)
if at_index is None:
return None
return at_index.data
def insert(self, index, data):
""" Inserts data at a specific index
"""
if index == 0:
self.prepend(data)
return
at_index = self._at_index(index - 1)
if at_index is None:
raise IndexError
if at_index.next is None:
self.append(data)
return
new_node = Node(data)
if at_index.next is not None:
at_index.next.last = new_node
new_node.next = at_index.next
at_index.next = new_node
new_node.last = at_index
def find(self, quality):
"""Return an item from this linked list satisfying the given quality"""
current = self.head
while current is not None:
if quality(current.data): # We no know
return current.data
current = current.next
def reverse(self):
next_node = self.head.next
previous_node = self.head
self.tail = previous_node
self.head.next = None
while next_node is not None:
current_node = next_node
next_node = current_node.next
previous_node.last = current_node
current_node.next = previous_node
previous_node = current_node
self.head = previous_node
self.head.last = None
def _find(self, data):
""" Finds a node with data or returns None if we can't find a node """
current = self.head
while current is not None:
if current.data == data:
return current
current = current.next
def replace(self, old_data, new_data):
""" Replaces data with new data """
old_node = self._find(old_data)
if old_node is None:
raise ValueError
old_node.data = new_data
def test_DoublyLinkedList():
dll = DoublyLinkedList()
print(dll)
print('Appending items:')
dll.append('A')
print(dll)
dll.append('B')
print(dll)
dll.append('C')
print(dll)
print('head: {}'.format(dll.head))
print('tail: {}'.format(dll.tail))
print('size: {}'.format(dll.size))
print('length: {}'.format(dll.length()))
print('Deleting items:')
dll.delete('B')
print(dll)
dll.delete('C')
print(dll)
dll.delete('A')
print(dll)
print('head: {}'.format(dll.head))
print('tail: {}'.format(dll.tail))
print('size: {}'.format(dll.size))
print('length: {}'.format(dll.length()))
print("testing: DoublyLinkedList replace ___________________")
dll = DoublyLinkedList(['A', 'B', 'C'])
dll.replace('A', 'D')
print(dll)
dll = DoublyLinkedList(['A', 'B', 'C'])
print(dll)
print("testing: insert_at_index ___________________")
print('size: {}'.format(dll.size))
dll.insert(0, 'AA')
print(dll)
print("testing: insert_at_index 0, 'AA'___________________")
dll.insert(2, 'BB')
print("testing: insert_at_index 2, 'BB'___________________")
print(dll)
if __name__ == '__main__':
test_DoublyLinkedList()
#!python
import DoublyLinkedList, Node
import unittest
class NodeTest(unittest.TestCase):
def test_init(self):
data = 'ABC'
node = Node(data)
assert node.data is data
assert node.next is None
class LinkedListTest(unittest.TestCase):
def test_init(self):
dll = DoublyLinkedList()
assert dll.head is None
assert dll.tail is None
def test_init_with_list(self):
dll = DoublyLinkedList(['A', 'B', 'C'])
assert dll.head.data == 'A'
assert dll.tail.data == 'C'
def test_items(self):
dll = DoublyLinkedList()
assert dll.items() == []
dll.append('A')
assert dll.items() == ['A']
dll.append('B')
assert dll.items() == ['A', 'B']
dll.append('C')
assert dll.items() == ['A', 'B', 'C']
def test_append(self):
dll = DoublyLinkedList()
dll.append('A')
assert dll.head.data == 'A'
assert dll.tail.data == 'A'
dll.append('B')
assert dll.head.data == 'A'
assert dll.tail.data == 'B'
dll.append('C')
assert dll.head.data == 'A'
assert dll.tail.data == 'C'
def test_prepend(self):
dll = DoublyLinkedList()
dll.prepend('C')
assert dll.head.data == 'C'
assert dll.tail.data == 'C'
dll.prepend('B')
assert dll.head.data == 'B'
assert dll.tail.data == 'C'
dll.prepend('A')
assert dll.head.data == 'A'
assert dll.tail.data == 'C'
def test_delete(self):
dll = DoublyLinkedList()
dll.append('A')
dll.append('B')
dll.append('C')
dll.delete('A')
assert dll.head.data == 'B'
assert dll.tail.data == 'C'
dll.delete('C')
assert dll.head.data == 'B'
assert dll.tail.data == 'B'
dll.delete('B')
assert dll.head is None
assert dll.tail is None
with self.assertRaises(ValueError):
dll.delete('D')
def test_find(self):
dll = DoublyLinkedList()
dll.append('A')
dll.append('B')
dll.append('C')
assert dll.find(lambda item: item == 'B') == 'B'
assert dll.find(lambda item: item < 'B') == 'A'
assert dll.find(lambda item: item > 'B') == 'C'
assert dll.find(lambda item: item == 'D') is None
def test_reverse_three(self):
dll = DoublyLinkedList()
dll.append('1')
dll.append('2')
dll.append('3')
assert dll.items() == ['1', '2', '3']
dll.reverse()
assert dll.items() == ['3', '2', '1']
def test_reverse_two(self):
dll = DoublyLinkedList()
dll.append('1')
dll.append('2')
assert dll.items() == ['1', '2']
dll.reverse()
assert dll.items() == ['2', '1']
def test_reverse_one(self):
dll = DoublyLinkedList()
dll.append('1')
assert dll.items() == ['1']
dll.reverse()
assert dll.items() == ['1']
def test_size(self):
dll = DoublyLinkedList()
assert dll.size() == 0
dll.append('4')
dll.append('3')
assert dll.size() == 2
dll.delete('4')
assert dll.size() == 1
dll.delete('3')
assert dll.size() == 0
def test_at_index(self):
dll = DoublyLinkedList()
dll.append('4')
dll.append('3')
assert dll.at_index(-1) is None
assert dll.at_index(0) == '4'
assert dll.at_index(1) == '3'
assert dll.at_index(2) is None
dll.delete('4')
assert dll.at_index(0) == '3'
def test_insert(self):
dll = DoublyLinkedList()
dll.append('4')
dll.append('3')
dll.insert(0, '2')
assert dll.items() == ['2', '4', '3']
dll.insert(3, '9')
assert dll.items() == ['2', '4', '3', '9']
dll.insert(2, '8')
assert dll.items() == ['2', '4', '8', '3', '9']
def test_last(self):
dll = DoublyLinkedList()
dll.append('4')
dll.append('3')
assert dll._at_index(0).last is None
assert dll._at_index(1).last.data == '4'
dll.insert(1, '7')
assert dll._at_index(2).last.data == '7'
assert dll._at_index(1).last.data == '4'
dll.delete('3')
assert dll._at_index(1).last.data == '4'
def test_replace(self):
dll = DoublyLinkedList()
dll.append('4')
dll.append('3')
dll.append('2')
assert dll.items() == ['4', '3', '2']
dll.replace('3', '9')
assert dll.items() == ['4', '9', '2']
dll.replace('4', '5')
assert dll.items() == ['5', '9', '2']
dll.replace('2', '1')
assert dll.items() == ['5', '9', '1']
with self.assertRaises(ValueError):
dll.replace('99999', '1')
if __name__ == '__main__':
unittest.main()
replace(item, replacement)
to double linkage. You can refine your bug derivingLinkedList
fromNode
(or a common base, SimSet style). \$\endgroup\$