From 0c1872433e44249c32a451bec1a85f04ac73505b Mon Sep 17 00:00:00 2001 From: davidanagy Date: Fri, 27 Mar 2020 09:17:52 -0700 Subject: [PATCH 1/6] finished MVP of ring_buffer.py, though I'm not entirely satisfied with the solution --- ring_buffer/ring_buffer.py | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/ring_buffer/ring_buffer.py b/ring_buffer/ring_buffer.py index ef88f0d6b..c3bc0f4c4 100644 --- a/ring_buffer/ring_buffer.py +++ b/ring_buffer/ring_buffer.py @@ -1,4 +1,4 @@ -from doubly_linked_list import DoublyLinkedList +from doubly_linked_list import DoublyLinkedList, ListNode class RingBuffer: @@ -8,14 +8,45 @@ def __init__(self, capacity): self.storage = DoublyLinkedList() def append(self, item): - pass + if self.storage.length == 0: + self.storage.add_to_head(item) + self.current = self.storage.head + elif self.storage.length < self.capacity: + self.storage.add_to_tail(item) + else: + if self.current is self.storage.head: + self.storage.remove_from_head() + self.storage.add_to_head(item) + if self.storage.head.next: + self.current = self.storage.head.next + else: + self.current = self.storage.head + elif self.current is self.storage.tail: + self.storage.remove_from_tail() + self.storage.add_to_tail(item) + self.current = self.storage.head + else: + current_next = self.current.next + current_prev = self.current.prev + self.storage.delete(self.current) + new_node = ListNode(item) + new_node.next = current_next + new_node.prev = current_prev + current_next.prev = new_node + current_prev.next = new_node + self.storage.length += 1 + self.current = new_node.next def get(self): # Note: This is the only [] allowed list_buffer_contents = [] # TODO: Your code here - + node = self.storage.head + list_buffer_contents.append(node.value) + while node.next: + node = node.next + list_buffer_contents.append(node.value) return list_buffer_contents # ----------------Stretch Goal------------------- From 9eec4450129dd37406cfd0dacbdee0beee33223c Mon Sep 17 00:00:00 2001 From: davidanagy Date: Fri, 27 Mar 2020 09:32:55 -0700 Subject: [PATCH 2/6] finished names.py, with a runtime of 0.1 seconds on my computer and a runtime complexity of (I believe) O(n log n). --- names/names.py | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/names/names.py b/names/names.py index ea158997f..f4bfedb0c 100644 --- a/names/names.py +++ b/names/names.py @@ -13,10 +13,31 @@ duplicates = [] # Return the list of duplicates in this data structure # Replace the nested for loops below with your improvements -for name_1 in names_1: - for name_2 in names_2: - if name_1 == name_2: - duplicates.append(name_1) + +# The runtime of the original code here was O(n^2) +# for name_1 in names_1: +# for name_2 in names_2: +# if name_1 == name_2: +# duplicates.append(name_1) + +# The below imports assume that the given folders contain the requisite +# data structures. +import sys +sys.path.append('../../Data-Structures/binary_search_tree') +sys.path.append('../../Data-Structures/queue_and_stack') +sys.path.append('../../Data-Structures/doubly_linked_list') +from binary_search_tree import BinarySearchTree + +# This has a runtime complexity of O(n log n), I think. +# It took about 0.1 seconds to run on my computer. +tree = BinarySearchTree(names_1[0]) +for name_1 in names_1[1:]: + tree.insert(name_1) +for name_2 in names_2: + # The "contains" method checks if the value exists in the tree. + # On average, its runtime is O(log n). + if tree.contains(name_2): + duplicates.append(name_2) end_time = time.time() print (f"{len(duplicates)} duplicates:\n\n{', '.join(duplicates)}\n\n") From 38210e19b652313a29d895e6600f434ccfb00b11 Mon Sep 17 00:00:00 2001 From: davidanagy Date: Fri, 27 Mar 2020 09:50:10 -0700 Subject: [PATCH 3/6] completed reverse.py with a runtime complexity of O(n) --- reverse/reverse.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/reverse/reverse.py b/reverse/reverse.py index 825d00556..29ef86229 100644 --- a/reverse/reverse.py +++ b/reverse/reverse.py @@ -47,4 +47,26 @@ def contains(self, value): def reverse_list(self, node, prev): # You must use recursion for this solution - pass + + # Case where list is empty; do nothing + if not self.head: + pass + # Base case + elif not node.get_next(): + # This means the node is the original tail, + # so make it the new head. + # (This also applies if the list has one element, + # since then the head just is the tail.) + self.head = node + # In all cases, the "prev" argument contains the node's + # old previous node. Since we're reversing, we want this + # to be its next node. + node.set_next(prev) + else: + # Save the node's current next node; we'll be passing + # it through the function soon. + old_next = node.get_next() + node.set_next(prev) + # Re-run the function on "old_next", with the "prev" argument + # being "node" for reasons outlined above. + self.reverse_list(old_next, node) From 86bb54cd9655c8333cd54d1bd89c7c2d64a589d1 Mon Sep 17 00:00:00 2001 From: davidanagy Date: Fri, 27 Mar 2020 10:01:29 -0700 Subject: [PATCH 4/6] completed stretch goal in ring_buffer.py --- ring_buffer/ring_buffer.py | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/ring_buffer/ring_buffer.py b/ring_buffer/ring_buffer.py index c3bc0f4c4..05f088ab9 100644 --- a/ring_buffer/ring_buffer.py +++ b/ring_buffer/ring_buffer.py @@ -54,10 +54,30 @@ def get(self): class ArrayRingBuffer: def __init__(self, capacity): - pass + self.capacity = capacity + # Initialize current at 0 because that'll be the + # first value that gets replaced. + self.current = 0 + # Looking at the test file, it wants the storage + # to always have capacity values. So initaliaze + # with only "None"s. + self.storage = [None] * capacity def append(self, item): - pass + # Replace the item in location "self.current" + self.storage[self.current] = item + if self.current < self.capacity - 1: + # Add one to current if it's not at the end of the list + self.current += 1 + else: + # If it is at the end of the list, set it back to 0. + self.current = 0 def get(self): - pass + storage_print = [] + for item in self.storage: + # Only return items that aren't "None." + if item: + storage_print.append(item) + + return storage_print From aaa06f7d06e696510f7173aeeacaf58efdd31854 Mon Sep 17 00:00:00 2001 From: davidanagy Date: Fri, 27 Mar 2020 10:16:57 -0700 Subject: [PATCH 5/6] finished stretch goal in names.py --- names/names.py | 47 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/names/names.py b/names/names.py index f4bfedb0c..121b7acd7 100644 --- a/names/names.py +++ b/names/names.py @@ -14,7 +14,7 @@ # Replace the nested for loops below with your improvements -# The runtime of the original code here was O(n^2) +# --> The runtime of the original code here was O(n^2) <-- # for name_1 in names_1: # for name_2 in names_2: # if name_1 == name_2: @@ -22,23 +22,42 @@ # The below imports assume that the given folders contain the requisite # data structures. +# import sys +# sys.path.append('../../Data-Structures/binary_search_tree') +# sys.path.append('../../Data-Structures/queue_and_stack') +# sys.path.append('../../Data-Structures/doubly_linked_list') +# from binary_search_tree import BinarySearchTree + +# # This has a runtime complexity of O(n log n), I think. +# # It took about 0.1 seconds to run on my computer. +# tree = BinarySearchTree(names_1[0]) +# for name_1 in names_1[1:]: +# tree.insert(name_1) +# for name_2 in names_2: +# # The "contains" method checks if the value exists in the tree. +# # On average, its runtime is O(log n). +# if tree.contains(name_2): +# duplicates.append(name_2) + +# STRETCH SOLUTION +# Basically I want to search names_1 for each name_2, and add to +# "duplicates" if I find it. +# The most efficient search with arrays is a Binary Search, +# so I'll import my code for that. import sys -sys.path.append('../../Data-Structures/binary_search_tree') -sys.path.append('../../Data-Structures/queue_and_stack') -sys.path.append('../../Data-Structures/doubly_linked_list') -from binary_search_tree import BinarySearchTree - -# This has a runtime complexity of O(n log n), I think. -# It took about 0.1 seconds to run on my computer. -tree = BinarySearchTree(names_1[0]) -for name_1 in names_1[1:]: - tree.insert(name_1) +sys.path.append('../../Sorting/src/searching') +from searching import binary_search +# To do a binary search, you first have to sort the search array. +names_1.sort() for name_2 in names_2: - # The "contains" method checks if the value exists in the tree. - # On average, its runtime is O(log n). - if tree.contains(name_2): + # binary_search returns a -1 when the item isn't found, + # so append to duplicates whenever the result isn't -1. + if binary_search(names_1, name_2) != -1: duplicates.append(name_2) +# The runtime for this ended up being about 0.8 seconds on my computer-- +# better than the Binary Search Tree! + end_time = time.time() print (f"{len(duplicates)} duplicates:\n\n{', '.join(duplicates)}\n\n") print (f"runtime: {end_time - start_time} seconds") From 084c23e5709de948bc8973e4057a80ab15c8b380 Mon Sep 17 00:00:00 2001 From: davidanagy Date: Fri, 27 Mar 2020 10:49:51 -0700 Subject: [PATCH 6/6] made the ring_buffer.py code better --- ring_buffer/ring_buffer.py | 45 ++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/ring_buffer/ring_buffer.py b/ring_buffer/ring_buffer.py index 05f088ab9..9c10c0103 100644 --- a/ring_buffer/ring_buffer.py +++ b/ring_buffer/ring_buffer.py @@ -1,4 +1,4 @@ -from doubly_linked_list import DoublyLinkedList, ListNode +from doubly_linked_list import DoublyLinkedList class RingBuffer: @@ -8,34 +8,48 @@ def __init__(self, capacity): self.storage = DoublyLinkedList() def append(self, item): - if self.storage.length == 0: - self.storage.add_to_head(item) - self.current = self.storage.head - elif self.storage.length < self.capacity: + if not self.current: + # If self.current is None, the list isn't full yet self.storage.add_to_tail(item) + if self.storage.length == self.capacity: + # If this puts the list at capacity, set self.current + # to the head. (self.current is basically the node + # that will get replaced if a new value is appended.) + self.current = self.storage.head else: if self.current is self.storage.head: + # Replace the head + self.current = self.storage.head.next self.storage.remove_from_head() self.storage.add_to_head(item) - if self.storage.head.next: - self.current = self.storage.head.next - else: - self.current = self.storage.head elif self.current is self.storage.tail: + # Replace the tail + self.current = self.storage.head self.storage.remove_from_tail() self.storage.add_to_tail(item) - self.current = self.storage.head else: + # Log self.current's next and prev, then delete it. current_next = self.current.next current_prev = self.current.prev self.storage.delete(self.current) - new_node = ListNode(item) + # Set the new self.current + self.current = current_next + # Keep track of the tail, since we'll be adding the + # new value as the tail temporarily. + tail = self.storage.tail + self.storage.add_to_tail(item) + # Set the new node's next and prev to be the same as + # the old self.current. + new_node = self.storage.tail new_node.next = current_next new_node.prev = current_prev + # Also fix the directions for the nodes that surround the new node current_next.prev = new_node current_prev.next = new_node - self.storage.length += 1 - self.current = new_node.next + # Finally, set the tail back to being the proper tail, + # and make sure it has no "next" node. + self.storage.tail = tail + self.storage.tail.next = None def get(self): # Note: This is the only [] allowed @@ -43,10 +57,9 @@ def get(self): # TODO: Your code here node = self.storage.head - list_buffer_contents.append(node.value) - while node.next: - node = node.next + while node: list_buffer_contents.append(node.value) + node = node.next return list_buffer_contents # ----------------Stretch Goal-------------------