diff --git a/Pipfile b/Pipfile new file mode 100644 index 000000000..b5846df18 --- /dev/null +++ b/Pipfile @@ -0,0 +1,11 @@ +[[source]] +name = "pypi" +url = "https://pypi.org/simple" +verify_ssl = true + +[dev-packages] + +[packages] + +[requires] +python_version = "3.8" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 000000000..e42812c26 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,20 @@ +{ + "_meta": { + "hash": { + "sha256": "7f7606f08e0544d8d012ef4d097dabdd6df6843a28793eb6551245d4b2db4242" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.8" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": {}, + "develop": {} +} diff --git a/README.md b/README.md index f66e9e01c..1646f92f5 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,22 @@ Implement this behavior in the RingBuffer class. RingBuffer has two methods, `ap _You may not use a Python List in your implementation of the `append` method (except for the stretch goal)_ -*Stretch Goal*: Another method of implementing a ring buffer uses an array (Python List) instead of a linked list. What are the advantages and disadvantages of using this method? What disadvantage normally found in arrays is overcome with this arrangement? +_Stretch Goal_: Another method of implementing a ring buffer uses an array (Python List) instead of a linked list. What are the advantages and disadvantages of using this method? What disadvantage normally found in arrays is overcome with this arrangement? + +Using an array was much easier for me to visualize and implement, because I didn't have to deal with setting the head and tail. Initializing the array to have the number of elements equal to the capacity made it easy to keep track of the current index and swap values. + +Normally, adding to an array could cause more memory to have to be allocated if more memory is needed than what was initially allocated, but in this case, the capacity is fixed, so more memory won't be needed, so that is an advantage of this arrangement. + +As far as big O goes, linked lists and arrays have similar values for several methods. However, finding an element in a linked list has O(n) versus an array's O(1), so that is one advantage of an array implementation. + +| method | Array | DLL | +| --------- | :----------: | ------------: | +| append | 1 | 1 | +| print nth | 1 | n | +| add front | n | 1 | +| del front | n | 1 | +| del mid | n | 1 (n to find) | +| find | n (or log n) | n | For example: @@ -62,44 +77,46 @@ buffer.get() # should return ['d', 'e', 'f'] #### Task 2. Runtime Optimization -***!Important!*** If you are running this using PowerShell by clicking on the green play button, you will get an error that `names1.txt` is not found. To resolve this, run it, get the error, then `cd` into the `names` directory in the `python` terminal that opens in VSCode. +**_!Important!_** If you are running this using PowerShell by clicking on the green play button, you will get an error that `names1.txt` is not found. To resolve this, run it, get the error, then `cd` into the `names` directory in the `python` terminal that opens in VSCode. Navigate into the `names` directory. Here you will find two text files containing 10,000 names each, along with a program `names.py` that compares the two files and prints out duplicate name entries. Try running the code with `python3 names.py`. Be patient because it might take a while: approximately six seconds on my laptop. What is the runtime complexity of this code? Six seconds is an eternity so you've been tasked with speeding up the code. Can you get the runtime to under a second? Under one hundredth of a second? -*You may not use the built in Python list, set, or dictionary in your solution for this problem. However, you can and should use the provided `duplicates` list to return your solution.* +_You may not use the built in Python list, set, or dictionary in your solution for this problem. However, you can and should use the provided `duplicates` list to return your solution._ (Hint: You might try importing a data structure you built during the week) - #### Task 3. Reverse a Linked List Recursively -Inside of the `reverse` directory, you'll find a basic implementation of a Singly Linked List. _Without_ making it a Doubly Linked List (adding a tail attribute), complete the `reverse_list()` function within `reverse/reverse.py` reverse the contents of the list using recursion, *not a loop.* +Inside of the `reverse` directory, you'll find a basic implementation of a Singly Linked List. _Without_ making it a Doubly Linked List (adding a tail attribute), complete the `reverse_list()` function within `reverse/reverse.py` reverse the contents of the list using recursion, _not a loop._ For example, + ``` 1->2->3->None ``` + would become... + ``` 3->2->1->None ``` -While credit will be given for a functional solution, only optimal solutions will earn a ***3*** on this task. +While credit will be given for a functional solution, only optimal solutions will earn a **_3_** on this task. -#### Stretch - -* Say your code from `names.py` is to run on an embedded computer with very limited RAM. Because of this, memory is extremely constrained and you are only allowed to store names in arrays (i.e. Python lists). How would you go about optimizing the code under these conditions? Try it out and compare your solution to the original runtime. (If this solution is less efficient than your original solution, include both and label the strech solution with a comment) +#### Stretch +- Say your code from `names.py` is to run on an embedded computer with very limited RAM. Because of this, memory is extremely constrained and you are only allowed to store names in arrays (i.e. Python lists). How would you go about optimizing the code under these conditions? Try it out and compare your solution to the original runtime. (If this solution is less efficient than your original solution, include both and label the strech solution with a comment) ### Rubric -| OBJECTIVE | TASK | 1 - DOES NOT MEET Expectations | 2 - MEETS Expectations | 3 - EXCEEDS Expectations | SCORE | -| ---------- | ----- | ------- | ------- | ------- | -- | -| _Student should be able to construct a queue and stack and justify the decision to use a linked list instead of an array._ | Task 1. Implement a Ring Buffer Data Structure | Solution in `ring_buffer.py` DOES NOT run OR it runs but has multiple logical errors, failing 3 or more tests | Solution in `ring_buffer.py` runs, but may have one or two logical errors; passes at least 9/11 tests (Note that each _assert_ function is a test.) | Solution in `ring_buffer.py` has no syntax or logical errors and passes 11/11 tests (Note that each _assert_ function is a test.)| | -| _Student should be able to construct a binary search tree class that can perform basic operations with O(log n) runtime._ | Task 2. Runtime Optimization | Student does NOT correctly identify the runtime of the starter code in `name.py` and optimize it to run in under 6 seconds | Student does not identify the runtime of the starter code in `name.py`, but optimizes it to run in under 6 seconds, with a solution of O(n log n) or better | Student does BOTH correctly identify the runtime of the starter code in `name.py` and optimizes it to run in under 6 seconds, with a solution of 0(n log n) or better | | -| _Student should be able to construct a linked list and compare the runtime of operations to an array to make the optimal choice between them._ | Task 3. Reverse the contents of a Singly Linked List using Recursion| Student's solution in `reverse.py` is failing one or more tests | Student's solution in `reverse.py` is able to correctly print out the contents of the Linked List in reverse order, passing all tests, BUT, the runtime of their solution is not optimal (requires looping through the list more than once) | Student's solution in `reverse.py` is able to correctly print out the contents of the Linked List in reverse order, passing all tests AND it has a runtime of O(n) or better | | +| OBJECTIVE | TASK | 1 - DOES NOT MEET Expectations | 2 - MEETS Expectations | 3 - EXCEEDS Expectations | SCORE | +| ---------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | +| _Student should be able to construct a queue and stack and justify the decision to use a linked list instead of an array._ | Task 1. Implement a Ring Buffer Data Structure | Solution in `ring_buffer.py` DOES NOT run OR it runs but has multiple logical errors, failing 3 or more tests | Solution in `ring_buffer.py` runs, but may have one or two logical errors; passes at least 9/11 tests (Note that each _assert_ function is a test.) | Solution in `ring_buffer.py` has no syntax or logical errors and passes 11/11 tests (Note that each _assert_ function is a test.) | | +| _Student should be able to construct a binary search tree class that can perform basic operations with O(log n) runtime._ | Task 2. Runtime Optimization | Student does NOT correctly identify the runtime of the starter code in `name.py` and optimize it to run in under 6 seconds | Student does not identify the runtime of the starter code in `name.py`, but optimizes it to run in under 6 seconds, with a solution of O(n log n) or better | Student does BOTH correctly identify the runtime of the starter code in `name.py` and optimizes it to run in under 6 seconds, with a solution of 0(n log n) or better | | +| _Student should be able to construct a linked list and compare the runtime of operations to an array to make the optimal choice between them._ | Task 3. Reverse the contents of a Singly Linked List using Recursion | Student's solution in `reverse.py` is failing one or more tests | Student's solution in `reverse.py` is able to correctly print out the contents of the Linked List in reverse order, passing all tests, BUT, the runtime of their solution is not optimal (requires looping through the list more than once) | Student's solution in `reverse.py` is able to correctly print out the contents of the Linked List in reverse order, passing all tests AND it has a runtime of O(n) or better | | #### Passing the Sprint + Score ranges for a 1, 2, and 3 are shown in the rubric above. For a student to have _passed_ a sprint challenge, they need to earn an **average of at least 2** for all items on the rubric. diff --git a/names/binary_search_tree.py b/names/binary_search_tree.py new file mode 100644 index 000000000..6fee6aba1 --- /dev/null +++ b/names/binary_search_tree.py @@ -0,0 +1,225 @@ +from dll_stack import Stack +from dll_queue import Queue +from collections import deque + +''' +import sys +sys.path.append('../queue_and_stack') +''' + + +class BinarySearchTree: + def __init__(self, value): + self.value = value + self.left = None + self.right = None + + # Insert the given value into the tree + def insert(self, value): + while self: + # print("self: " + str(self)) + # print("value: " + str(value)) + if value < self.value: + if not self.left: + self.left = BinarySearchTree(value) + return + else: + self = self.left + else: + if not self.right: + self.right = BinarySearchTree(value) + return + else: + self = self.right + + def recursive_insert(self, value): + if value < self.value: + if self.left: + self.left.insert(value) + else: + self.left = BinarySearchTree(value) + else: + if self.right: + self.right.insert(value) + else: + self.right = BinarySearchTree(value) + + def contains(self, target): + # Return True if the tree contains the value + # False if it does not + while self: + # print(self.value) + # print(target) + if target == self.value: + return True + elif target < self.value: + if not self.left: + return False + else: + self = self.left + else: + if not self.right: + return False + else: + self = self.right + + # Return the maximum value found in the tree + + def get_max(self): + # Initially set the max value to be self. + max = self.value + while self.right: + if self.right.value > max: + max = self.right.value + self = self.right + return max + + # Call the function `cb` on the value of each node + # You may use a recursive or iterative approach + def for_each(self, cb): + cb(self.value) + if self.left and self.right: + self.left.for_each(cb) + self.right.for_each(cb) + elif self.left: + self.left.for_each(cb) + elif self.right: + self.right.for_each(cb) + + def for_each_lecture(self, cb): + cb(self.value) + # base case is when self has no left or right + if self.left: + self.left.for_each(cb) + if self.right: + self.right.for_each(cb) + + def for_each_iterative_depth_first(self, cb): + stack = [] + stack.append(self) + while len(stack) > 0: + current_node = stack.pop() + # Checking the right first will result in the same order as the + # recursive (lecture) version above + if current_node.right: + stack.append(current_node.right) + if current_node.left: + stack.append(current_node.left) + cb(current_node.value) + + def for_each_iterative_breadth_first(self, cb): + q = deque() + q.append(self) + while len(q) > 0: + current_node = q.popleft() + # for left to right ordering, check left first. + if current_node.left: + q.append(current_node.left) + if current_node.right: + q.append(current_node.right) + cb(current_node.value) + + # DAY 2 Project ----------------------- + + # Print all the values in order from low to high + # Hint: Use a recursive, depth first traversal + # AKA Inorder Traversal + # Recursively: + # 1. Visit left subtree + # 2. Visit node + # 3. Visit right subtree + + def in_order_print(self, node): + if node == None: + return + self.in_order_print(node.left) + print(node.value) + self.in_order_print(node.right) + + # Print the value of every node, starting with the given node, + # in an iterative breadth first traversal + + def bft_print(self, node): + q = Queue() + while node is not None: + print(node.value) + # Stick all of the node's children in the end of the queue. + if node.left: + q.enqueue(node.left) + if node.right: + q.enqueue(node.right) + if q.len() > 0: + # Get the first node in the queue and continue the loop with it. + node = q.dequeue() + else: + break + return + + # Print the value of every node, starting with the given node, + # in an iterative depth first traversal + + def dft_print(self, node): + s = Stack() + while node is not None: + print(node.value) + # Stick all of the node's children in the end of the stack. + if node.left: + s.push(node.left) + if node.right: + s.push(node.right) + if s.len() > 0: + # Get the last node in the stack and continue the loop with it. + node = s.pop() + else: + break + return + + # STRETCH Goals ------------------------- + # Note: Research may be required + + # Print Pre-order recursive DFT + ''' + To traverse a binary tree in preorder, + 1. Visit the root. + 2. Traverse the left sub tree of root. + 3. Traverse the right sub tree of root. + ''' + + def pre_order_dft(self, node): + if node == None: + return + print(node.value) + self.pre_order_dft(node.left) + self.pre_order_dft(node.right) + + ''' + To traverse a binary tree in postorder traversal, + 1. Traverse the left sub tree of root. + 2. Traverse the right sub tree of root. + 3. Visit the root. + ''' + # Print Post-order recursive DFT + + def post_order_dft(self, node): + if node == None: + return + + self.post_order_dft(node.left) + self.post_order_dft(node.right) + print(node.value) + + +''' +my_bst = BinarySearchTree(1) +my_bst.insert(8) +my_bst.insert(5) +my_bst.insert(7) +my_bst.insert(6) +my_bst.insert(3) +my_bst.insert(4) +my_bst.insert(2) +# my_bst.bft_print(my_bst) +# my_bst.dft_print(my_bst) +# my_bst.pre_order_dft(my_bst) +my_bst.post_order_dft(my_bst) +''' diff --git a/names/dll_queue.py b/names/dll_queue.py new file mode 100644 index 000000000..fd622afc9 --- /dev/null +++ b/names/dll_queue.py @@ -0,0 +1,32 @@ + +# import sys +# sys.path.append('../doubly_linked_list') +from doubly_linked_list import DoublyLinkedList + +# FIFO (first in, first out) + + +class Queue: + def __init__(self): + self.size = 0 + # Why is our DLL a good choice to store our elements? + # Pretty straight-forward to add to head & tail, and remove from head & tail. + # Doesn't need an up-front allocation of memory. + # O(1) for both sides for a DLL. + # self.storage = ? + self.storage = DoublyLinkedList() + + def enqueue(self, value): + # Add to tail + self.storage.add_to_tail(value) + self.size += 1 + + def dequeue(self): + # Remove head + value = self.storage.remove_from_head() + if self.len() > 0: + self.size -= 1 + return value + + def len(self): + return self.size diff --git a/names/dll_stack.py b/names/dll_stack.py new file mode 100644 index 000000000..855e0774b --- /dev/null +++ b/names/dll_stack.py @@ -0,0 +1,31 @@ +from doubly_linked_list import DoublyLinkedList +# import sys +# sys.path.append('../doubly_linked_list') + +# stack uses LIFO (last in, first out) +# versus queue, which uses FIFO (first in, first out) + + +class Stack: + def __init__(self): + self.size = 0 + # Why is our DLL a good choice to store our elements? + # Pretty straight-forward to add to head & tail, and remove from head & tail. + # Doesn't need an up-front allocation of memory. + # But in this case, time complexity is the same, whether you use a DLL or an array. ?? + self.storage = DoublyLinkedList() + + def push(self, value): + # add to tail + self.storage.add_to_tail(value) + self.size += 1 + + def pop(self): + # remove from tail + value = self.storage.remove_from_tail() + if self.len() > 0: + self.size -= 1 + return value + + def len(self): + return self.size diff --git a/names/doubly_linked_list.py b/names/doubly_linked_list.py new file mode 100644 index 000000000..60548f932 --- /dev/null +++ b/names/doubly_linked_list.py @@ -0,0 +1,157 @@ +"""Each ListNode holds a reference to its previous node +as well as its next node in the List.""" + + +class ListNode: + def __init__(self, value, prev=None, next=None): + self.value = value + self.prev = prev + self.next = next + + """Wrap the given value in a ListNode and insert it + after this node. Note that this node could already + have a next node it is point to.""" + + def insert_after(self, value): + current_next = self.next + self.next = ListNode(value, self, current_next) + if current_next: + current_next.prev = self.next + + """Wrap the given value in a ListNode and insert it + before this node. Note that this node could already + have a previous node it is point to.""" + + def insert_before(self, value): + current_prev = self.prev + self.prev = ListNode(value, current_prev, self) + if current_prev: + current_prev.next = self.prev + + """Rearranges this ListNode's previous and next pointers + accordingly, effectively deleting this ListNode.""" + + def delete(self): + if self.prev: + self.prev.next = self.next + if self.next: + self.next.prev = self.prev + + +"""Our doubly-linked list class. It holds references to +the list's head and tail nodes.""" + + +class DoublyLinkedList: + def __init__(self, node=None): + self.head = node + self.tail = node + self.length = 1 if node is not None else 0 + + def __len__(self): + return self.length + + """Wraps the given value in a ListNode and inserts it + as the new head of the list. Don't forget to handle + the old head node's previous pointer accordingly.""" + + def add_to_head(self, value): + new_node = ListNode(value) + self.length += 1 + if not self.head and not self.tail: + self.head = new_node + self.tail = new_node + else: + new_node.next = self.head + self.head.prev = new_node + self.head = new_node + + """Removes the List's current head node, making the + current head's next node the new head of the List. + Returns the value of the removed Node.""" + + def remove_from_head(self): + value = self.head.value + self.delete(self.head) + return value + + """Wraps the given value in a ListNode and inserts it + as the new tail of the list. Don't forget to handle + the old tail node's next pointer accordingly.""" + + def add_to_tail(self, value): + new_node = ListNode(value) + self.length += 1 + if not self.head and not self.tail: + self.head = new_node + self.tail = new_node + else: + new_node.prev = self.tail + self.tail.next = new_node + self.tail = new_node + + """Removes the List's current tail node, making the + current tail's previous node the new tail of the List. + Returns the value of the removed Node.""" + + def remove_from_tail(self): + value = self.tail.value + self.delete(self.tail) + return value + + """Removes the input node from its current spot in the + List and inserts it as the new head node of the List.""" + + def move_to_front(self, node): + if node is self.head: + return + self.add_to_head(node.value) + self.delete(node) + + """Removes the input node from its current spot in the + List and inserts it as the new tail node of the List.""" + + def move_to_end(self, node): + if node is self.tail: + return + self.add_to_tail(node.value) + self.delete(node) + + """Removes a node from the list and handles cases where + the node was the head or the tail""" + + def delete(self, node): + # Planning + # TODO: Do we need error checking if node not in list? + self.length -= 1 + # This is the only node + if self.head is self.tail: + self.head = None + self.tail = None + # It's the head + elif node is self.head: + self.head = node.next + node.delete() + # it's the tail + elif node is self.tail: + self.tail = node.prev + node.delete() + # it's in the middle + else: + node.delete() + + """Returns the highest value currently in the list""" + + def get_max(self): + # How to get max + # create max var + current = self.head + max = self.head.value + # Loop through nodes + while(current is not None): + # compare value in node to max found + if current.value > max: + max = current.value + current = current.next + # return max found + return max diff --git a/names/names.py b/names/names.py index ea158997f..d3f3feb48 100644 --- a/names/names.py +++ b/names/names.py @@ -1,26 +1,68 @@ import time +import os +import sys +from binary_search_tree import BinarySearchTree start_time = time.time() -f = open('names_1.txt', 'r') -names_1 = f.read().split("\n") # List containing 10000 names -f.close() +with open(os.path.join(sys.path[0], 'names_1.txt'), 'r') as f: + names_1 = f.read().split("\n") # List containing 10000 names -f = open('names_2.txt', 'r') -names_2 = f.read().split("\n") # List containing 10000 names -f.close() +# Uncomment below to use the bst: +''' +bst = BinarySearchTree('Heather') +for name in names_1: + bst.insert(name) +''' + +with open(os.path.join(sys.path[0], 'names_2.txt'), 'r') as f: + names_2 = f.read().split("\n") # List containing 10000 names duplicates = [] # Return the list of duplicates in this data structure -# Replace the nested for loops below with your improvements + +def binary_search(arr, target): + low = 0 + high = len(arr) - 1 + while low <= high: + middle = (low + high)//2 + if target == arr[middle]: + return middle + elif target < arr[middle]: + high = middle - 1 + elif target > arr[middle]: + low = middle + 1 + return -1 + + +# This implementation doesn't use the bst +# 0.08800506591796875 seconds +names_1.sort() +for name in names_2: + if binary_search(names_1, name) != -1: + duplicates.append(name) + + +# This is the original implementation: +# About 10 seconds +''' for name_1 in names_1: for name_2 in names_2: if name_1 == name_2: duplicates.append(name_1) +''' + +# This implementation uses the bst: +# 0.13100862503051758 seconds +''' +for name in names_2: + if bst.contains(name): + duplicates.append(name) +''' end_time = time.time() -print (f"{len(duplicates)} duplicates:\n\n{', '.join(duplicates)}\n\n") -print (f"runtime: {end_time - start_time} seconds") +print(f"{len(duplicates)} duplicates:\n\n{', '.join(duplicates)}\n\n") +print(f"runtime: {end_time - start_time} seconds") # ---------- Stretch Goal ----------- # Python has built-in tools that allow for a very efficient approach to this problem diff --git a/reverse/reverse.py b/reverse/reverse.py index 825d00556..d3c922384 100644 --- a/reverse/reverse.py +++ b/reverse/reverse.py @@ -47,4 +47,11 @@ def contains(self, value): def reverse_list(self, node, prev): # You must use recursion for this solution - pass + if node == None: + self.head = prev + else: + next_node = node.next_node + node.next_node = prev + prev = node + node = next_node + self.reverse_list(node, prev) diff --git a/reverse/test_reverse.py b/reverse/test_reverse.py index 7ef0b4bf0..36e4fba56 100644 --- a/reverse/test_reverse.py +++ b/reverse/test_reverse.py @@ -1,47 +1,46 @@ import unittest from reverse import LinkedList + class LinkedListTests(unittest.TestCase): - def setUp(self): - self.list = LinkedList() - - def test_add_to_head(self): - self.list.add_to_head(1) - self.assertEqual(self.list.head.value, 1) - self.list.add_to_head(2) - self.assertEqual(self.list.head.value, 2) - - def test_contains(self): - self.list.add_to_head(1) - self.list.add_to_head(2) - self.list.add_to_head(10) - self.assertTrue(self.list.contains(2)) - self.assertTrue(self.list.contains(10)) - self.assertFalse(self.list.contains(1000)) - - def test_empty_reverse(self): - self.list.reverse_list(self.list.head, None) - self.assertEqual(self.list.head, None) - - def test_single_reverse(self): - self.list.add_to_head(1) - self.list.reverse_list(self.list.head, None) - self.assertEqual(self.list.head.value, 1) - - def test_longer_reverse(self): - self.list.add_to_head(1) - self.list.add_to_head(2) - self.list.add_to_head(3) - self.list.add_to_head(4) - self.list.add_to_head(5) - self.assertEqual(self.list.head.value, 5) - self.list.reverse_list(self.list.head, None) - self.assertEqual(self.list.head.value, 1) - self.assertEqual(self.list.head.get_next().value, 2) - self.assertEqual(self.list.head.get_next().get_next().value, 3) - - - + def setUp(self): + self.list = LinkedList() + + def test_add_to_head(self): + self.list.add_to_head(1) + self.assertEqual(self.list.head.value, 1) + self.list.add_to_head(2) + self.assertEqual(self.list.head.value, 2) + + def test_contains(self): + self.list.add_to_head(1) + self.list.add_to_head(2) + self.list.add_to_head(10) + self.assertTrue(self.list.contains(2)) + self.assertTrue(self.list.contains(10)) + self.assertFalse(self.list.contains(1000)) + + def test_empty_reverse(self): + self.list.reverse_list(self.list.head, None) + self.assertEqual(self.list.head, None) + + def test_single_reverse(self): + self.list.add_to_head(1) + self.list.reverse_list(self.list.head, None) + self.assertEqual(self.list.head.value, 1) + + def test_longer_reverse(self): + self.list.add_to_head(1) + self.list.add_to_head(2) + self.list.add_to_head(3) + self.list.add_to_head(4) + self.list.add_to_head(5) + self.assertEqual(self.list.head.value, 5) + self.list.reverse_list(self.list.head, None) + self.assertEqual(self.list.head.value, 1) + self.assertEqual(self.list.head.get_next().value, 2) + self.assertEqual(self.list.head.get_next().get_next().value, 3) + if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/ring_buffer/ring_buffer.py b/ring_buffer/ring_buffer.py index ef88f0d6b..ecb0c8d08 100644 --- a/ring_buffer/ring_buffer.py +++ b/ring_buffer/ring_buffer.py @@ -8,13 +8,39 @@ def __init__(self, capacity): self.storage = DoublyLinkedList() def append(self, item): - pass + if len(self.storage) == 0: + self.storage.add_to_head(item) + self.current = self.storage.head + elif len(self.storage) < self.capacity: + self.current.insert_after(item) + self.storage.length += 1 + if len(self.storage) == self.capacity: + self.current = self.storage.head + else: + self.current = self.current.next + + else: + self.current.insert_after(item) + if self.current == self.storage.head: + self.storage.head = self.current.next + self.current = self.current.next + self.storage.delete(self.current.prev) + self.storage.length += 1 + + if self.current.next is None: + self.current = self.storage.head + else: + self.current = self.current.next def get(self): # Note: This is the only [] allowed list_buffer_contents = [] # TODO: Your code here + pointer = self.storage.head + while pointer: + list_buffer_contents.append(pointer.value) + pointer = pointer.next return list_buffer_contents @@ -23,10 +49,26 @@ def get(self): class ArrayRingBuffer: def __init__(self, capacity): - pass + self.capacity = capacity + self.current = None + self.storage = [None for i in range(0, capacity)] def append(self, item): - pass + print(self.storage) + if self.current is None: + self.storage[0] = item + self.current = 1 + else: + self.storage[self.current] = item + self.current += 1 + if self.current == self.capacity: + self.current = 0 + print(self.storage) + print('\n') def get(self): - pass + list_buffer_contents = [] + for item in self.storage: + if item is not None: + list_buffer_contents.append(item) + return list_buffer_contents diff --git a/ring_buffer/test_ring_buffer.py b/ring_buffer/test_ring_buffer.py index 3eb2bda1f..2a24a62b1 100644 --- a/ring_buffer/test_ring_buffer.py +++ b/ring_buffer/test_ring_buffer.py @@ -15,6 +15,7 @@ def test_ring_buffer(self): self.buffer.append('c') self.buffer.append('d') self.assertEqual(self.buffer.storage.length, 4) + self.assertEqual(self.buffer.get(), ['a', 'b', 'c', 'd']) self.buffer.append('e') @@ -22,9 +23,10 @@ def test_ring_buffer(self): self.assertEqual(self.buffer.get(), ['a', 'b', 'c', 'd', 'e']) self.buffer.append('f') - self.assertEqual(self.buffer.storage.length, 5) self.assertEqual(self.buffer.get(), ['f', 'b', 'c', 'd', 'e']) + self.assertEqual(self.buffer.storage.length, 5) + self.buffer.append('g') self.buffer.append('h') self.buffer.append('i') @@ -53,10 +55,12 @@ def test__array_ring_buffer(self): self.buffer.append('c') self.buffer.append('d') self.assertEqual(len(self.buffer.storage), 5) + self.assertEqual(self.buffer.get(), ['a', 'b', 'c', 'd']) self.buffer.append('e') self.assertEqual(len(self.buffer.storage), 5) + self.assertEqual(self.buffer.get(), ['a', 'b', 'c', 'd', 'e']) self.buffer.append('f')